Замыкания
Замыкание — функция, которая запоминает переменные из своей внешней области видимости, даже когда внешняя функция уже завершилась.
Простой пример
function outer() {
const message = 'Привет';
function inner() {
console.log(message);
}
return inner;
}
const fn = outer();
fn(); // Привет
outer завершилась, но inner всё ещё имеет доступ к message.
Счётчик
function createCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
get: () => count,
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.get(); // 2
count недоступен снаружи, но живёт между вызовами.
Приватные данные
function createUser(name) {
let password = '';
return {
name,
setPassword(pwd) {
password = pwd;
},
checkPassword(pwd) {
return password === pwd;
},
};
}
const user = createUser('Алиса');
user.setPassword('secret');
user.checkPassword('secret'); // true
// user.password — undefined
password скрыт. Это эмуляция приватных полей до появления # в классах.
Замыкание в цикле
function makeFunctions() {
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i);
}
return fns;
}
const fns = makeFunctions();
fns[0](); // 0
fns[1](); // 1
fns[2](); // 2
С let каждая итерация имеет свою i.
С var — все функции видели бы одну i = 3.
Функция-множитель
function multiply(a) {
return (b) => a * b;
}
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15
double и triple — замыкания с разными a.
Мемоизация
function memoize(fn) {
const cache = new Map();
return (arg) => {
if (cache.has(arg)) return cache.get(arg);
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
const slowSquare = (n) => {
console.log('вычисление');
return n * n;
};
const fastSquare = memoize(slowSquare);
fastSquare(5); // вычисление, 25
fastSquare(5); // 25, без лога
cache живёт между вызовами.
Обработчики событий
function attachHandlers() {
const buttons = document.querySelectorAll('button');
buttons.forEach((button, index) => {
button.addEventListener('click', () => {
console.log(`Кнопка ${index}`);
});
});
}
Каждый обработчик «запоминает» свой index.
Один раз
function once(fn) {
let called = false;
let result;
return (...args) => {
if (!called) {
called = true;
result = fn(...args);
}
return result;
};
}
const init = once(() => {
console.log('инициализация');
return { ready: true };
});
init(); // лог + { ready: true }
init(); // { ready: true }, без лога
Утечки памяти
Замыкания держат переменные в памяти, пока жива функция:
function big() {
const data = new Array(1000000).fill(0);
return () => data.length;
}
const fn = big(); // data в памяти
Если функция не нужна — обнули ссылку:
let fn = big();
fn = null; // data освободится
Итоги
- Замыкание — функция + её внешние переменные
- Переменные живут, пока жива функция
- Приватные данные через замыкание
letв цикле даёт своё замыкание на итерацию- Мемоизация,
once, обработчики — на замыканиях - Следи за утечками