Разберись, как замыкания захватывают переменные, чтобы инкапсулировать состояние и не попадаться на ловушки областей видимости.
Открыть этот урок в KodokonЗамыкание - это не какой-то экзотический трюк: это механизм, благодаря которому функция сохраняет доступ к той лексической области, где она была определена, а не к той, где её вызвали. Движок не копирует значения: он хранит живую ссылку на лексическое окружение. Поэтому два вызова одной и той же фабрики создают два независимых окружения, и это делает замыкания самым лёгким инструментом инкапсуляции в языке.
function createCounter() {
let count = 0;
return {
increment: () => ++count,
reset: () => { count = 0; },
};
}
const counterA = createCounter();
const counterB = createCounter();
counterA.increment();
counterA.increment();
console.log(counterA.increment()); // 3
console.log(counterB.increment()); // 1Классическая ловушка из реальной жизни: var привязан к охватывающей функции, а не к блоку. Внутри цикла каждое замыкание захватывает тогда одну и ту же переменную. С let каждая итерация создаёт свежее лексическое окружение, поэтому каждый захват отдельный. Добавь к этому временную мёртвую зону (TDZ): переменная let или const существует с момента входа в блок, но выбрасывает ReferenceError, если прочитать её до объявления.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 0, 1, 2
}С точки зрения паттернов замыкания лежат в основе мемоизации и модульного паттерна. По сравнению с классом замыкание даёт настоящую приватность без this и прототипов, ценой чуть большего расхода памяти, когда ты создаёшь тысячи экземпляров: каждый экземпляр несёт свои собственные функции.
function memoize(fn) {
const cache = new Map();
return function (key) {
if (cache.has(key)) return cache.get(key);
const result = fn(key);
cache.set(key, result);
return result;
};
}
const square = (n) => n * n;
const fastSquare = memoize(square);
console.log(fastSquare(9)); // computed
console.log(fastSquare(9)); // read from the cachefor (var i = ...) что выведут три колбэка setTimeout, созданные внутри тела цикла?createCounter гарантирует, что count остаётся приватным?