Два полезных примера с замыканиями

Два полезных примера с замыканиями

Для тех, кто хочет чего-то необычного

В прошлой статье мы разобрали, что такое замыкание в программировании и зачем оно нужно. Сегодня будет практика — добавим замыкания в свои программы.

Делаем форматированый лог

Часто при отладке программы или для ведения логов в JavaScript используют команду console.log(). Она берёт строку из скобок и просто выводит её на экран, а такие сообщения называются логом. Но иногда бывает так, что нужно вывести ещё порядковый номер каждого сообщения и время, прошедшее с загрузки скрипта.

Если пойти стандартным путём, можно сделать так: завести функцию, которая выводит в лог сообщение, и глобальные переменные, где хранится служебная информация:

// стандартная функция
function log(timespan, lineNumber, msg) { 
	// выводим порядковый номер, время и сообщение
  console.log(lineNumber + "				" + timespan + "				" + msg);
}

// объявляем глобальные переменные для времени старта скрипта и порядкового номера
var start = Date.now();
var lineNumber = 1;

// выводим заголовок лога и линию отбивки
console.log('Номер  Время 		Сообщение')
console.log('---------------------------------')
// вызываем функцию для вывода сообщений
log(Date.now()-start, lineNumber++, "Первое сообщение");
log(Date.now()-start, lineNumber++, "Второе сообщение");
Два полезных примера с замыканиями

Но у такого подхода есть два минуса: 

  • в нём используется глобальная переменная, которую может поменять любая другая функция;
  • и нам нужно передавать при вызове каждый раз много параметров.

Вторую проблему можно решить, перенеся код вычисления даты и номера внутрь функции, но первую проблему это всё равно не решит.

Если сделать то же самое на замыканиях, то и код получится проще, и вызывать станет удобнее. А заодно решится проблема с глобальной переменной — их просто не будет, а значит, меньше шансов, что другая функция их случайно поменяет.

// функция с замыканием, которая будет отвечать за лог
var log = (function () {   
	// две переменные ниже недоступны из программы, 
	// поэтому никто их случайно не поменяет
	// сразу получаем время старта
  var start = Date.now(); 
  // и задаём начальный номер для сообщений
  var num = 1;   
  // выводим заголовок лога и линию отбивки
	console.log('Номер  Время 		Сообщение')
	console.log('---------------------------------')       
  // результат — функция, которая получает только один параметр на входе
  return function (msg) {  
  	// выводим уже отформатированный лог в консоль
  	// сразу же считаем прошедшее время, используя переменную start,
  	// а также увеличиваем на единицу после использования переменную num
    console.log(num++ + "				" + (Date.now()-start) + "				" + msg);
   }
// тут же запускаем первую функцию, чтобы вывести шапку лога и запомнить время старта
})(); 

// теперь можем выводить сообщения в консоль удобным способом
log("Первое сообщение");
log("Второе сообщение");
Два полезных примера с замыканиями

Приватные переменные без ООП

В объектно-ориентированном программировании часто пользуются приватными переменными и методами — к ним можно получить доступ только внутри класса, а снаружи этого сделать не получится. С приватными переменными можно работать только по определённым правилам — это сильно уменьшает риск ошибки при работе программы или при обработке данных. 

Работа с приватными переменными похожа на обслуживание в банке или МФЦ — нельзя подойти к окошку и обслужиться без талона, нужно его обязательно получить в автомате на входе.

В ООП всё просто: создаётся класс, в котором с помощью символа # обозначается такая переменная, и пишутся методы, которые позволяют с ней работать:

// создаём новый класс
class counter {
	// приватная переменная 
	#num = 1;

	// увеличиваем значение на 1
  plus() {
    this.#num += 1;
  }

  // уменьшаем значение на 1
  minus() {
  	this.#num -= 1;
  }

  // возвращаем текущее значение переменной
  value() {
  	return this.#num
  }

}

// создаём новый объект на основе класса
var c = new counter;
// выводим значение счётчика
console.log(c.value());
// увеличиваем на единицу
c.plus();
// выводим новое значение счётчика
console.log(c.value());
// а вот так мы не получим ничего
console.log(c.num);
Два полезных примера с замыканиями

Но что если мы не хотим работать в ООП-стиле и не хотим использовать классы? В этом случае можно сделать всё то же самое на замыканиях — и методы, и приватные переменные:

// функция с замыканием
const counter = () => {
  // приватная переменная num
  let num = 1;
  
  // результат — две функции, каждая из которых делает что-то своё
  return {

    // увеличиваем счётчик на 1
    plus() {
      num += 1;
    },
    
    // получаем значение счётчика
    value() {
      return num;
    },
  };
};

// создаём новую переменную на основе функции
var c = counter();
// выводим значение счётчика
console.log(c.value());
// увеличиваем на единицу
c.plus();
// выводим новое значение счётчика
console.log(c.value());
// а вот так мы не получим ничего
console.log(c.num);

Два полезных примера с замыканиями

Получается, что с замыканиями мы можем получить некоторые возможности ООП, не используя классы и логику работы с объектами.

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Даня Берковский

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Алина Грызлова

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
medium