Замыкания
Функция в JavaScript помнит переменные того места, где её создали, — даже когда это место уже завершило работу. Такую функцию вместе с запомненными переменными называют замыканием. На замыканиях построены счётчики, настройки и многие приёмы в библиотеках.
Функция внутри функции
Функцию можно объявить внутри другой функции и вернуть через return, как любое значение:
function makeGreeter() {
return () => 'Привет!';
}
const greet = makeGreeter();
console.log(greet()); // Привет!makeGreeter() возвращает функцию, мы сохраняем её в greet и вызываем уже её.
Функция помнит внешние переменные
Внутренняя функция видит переменные внешней — это мы знаем из урока об области видимости. Удивительно другое: она видит их и после того, как внешняя функция завершилась:
function makeGreeter(name) {
return () => `Привет, ${name}!`;
}
const greetAnna = makeGreeter('Анна');
console.log(greetAnna()); // Привет, Анна!Вызов makeGreeter('Анна') давно закончился, но возвращённая функция всё ещё помнит name. Это и есть замыкание.
Счётчик
Замыкание может не только читать запомненную переменную, но и менять её. Так получается счётчик, который хранит своё состояние между вызовами:
function makeCounter() {
let count = 0;
return () => {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3Переменная count живёт, пока существует функция counter.
У каждого вызова своё окружение
Каждый вызов внешней функции создаёт новый набор переменных. Поэтому два счётчика не мешают друг другу:
function makeCounter() {
let count = 0;
return () => ++count;
}
const a = makeCounter();
const b = makeCounter();
a();
a();
console.log(a()); // 3
console.log(b()); // 1Фабрика функций
Функцию, которая создаёт и возвращает другие функции, называют фабрикой. Она избавляет от повторов, когда нужно несколько похожих функций с разными настройками:
function makeMultiplier(factor) {
return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15Скрытые данные
До переменной внутри замыкания нельзя добраться снаружи — только через возвращённую функцию. Так данные защищают от случайных изменений: в примере со счётчиком никто не сможет присвоить count отрицательное число, его можно только увеличивать.
Частая ошибка
Каждый вызов makeCounter() создаёт новый счётчик с нулём. Если вызывать внешнюю функцию каждый раз заново, счёт не накапливается:
function makeCounter() {
let count = 0;
return () => ++count;
}
console.log(makeCounter()()); // 1
console.log(makeCounter()()); // 1Создайте счётчик один раз, сохраните его в переменную и вызывайте уже её: const counter = makeCounter();.