Что такое замыкание (closure)? Объясните на примере
Главная тема почти любого собеседования по JavaScript.
Замыкание (closure) — функция вместе с «запомненным» окружением: она сохраняет доступ к переменным той области, где была создана, даже после того как внешняя функция завершилась.
Суть на примере
Внутренняя функция «видит» переменные внешней. Когда мы возвращаем внутреннюю функцию наружу, она уносит с собой ссылку на эти переменные — они не исчезают, пока жива функция.
function makeCounter() {
let count = 0; // приватная переменная
return function () {
count++; // замыкание помнит count
return count;
};
}
const counter = makeCounter();
console.log(counter());
console.log(counter());
console.log(counter());
const another = makeCounter(); // своё, независимое окружение
console.log(another());
Вывод:
1 2 3 1
Каждый вызов makeCounter создаёт новое окружение со своим count. Поэтому another начинает с нуля — это и есть «независимые замыкания».
Зачем это нужно: приватные данные
Через замыкание делают приватность: переменную нельзя прочитать снаружи напрямую, только через возвращённые методы. Это паттерн «модуль».
function createBankAccount(start) {
let balance = start; // снаружи недоступна
return {
deposit(sum) { balance += sum; return balance; },
getBalance() { return balance; },
};
}
const acc = createBankAccount(100);
acc.deposit(50);
console.log(acc.getBalance());
console.log(acc.balance); // напрямую — недоступно
Вывод:
150 undefined
Замыкание запоминает аргументы
Часто на собеседовании просят написать функцию, которая «настраивается» аргументом. Это тоже замыкание.
function multiplier(factor) {
return (n) => n * factor; // factor «запомнен»
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5));
console.log(triple(5));
Вывод:
10 15
Как объяснить замыкание на собеседовании
Чёткая формулировка, которую ценят интервьюеры: «Замыкание — это функция, которая помнит переменные из области, где была объявлена (а не где вызвана). Эти переменные не удаляются сборщиком мусора, пока существует ссылающаяся на них функция». Полезно добавить, что в JavaScript замыкания возникают автоматически — каждая функция замыкается над своим лексическим окружением, отдельный синтаксис для этого не нужен.
Частый дополнительный вопрос — «где замыкания используются на практике?». Хорошие примеры: приватные поля до появления #private в классах, фабрики функций и колбэков, мемоизация (кэш хранится в замкнутой переменной), а также debounce и throttle, которые держат таймер в замыкании. Подвох, о котором стоит упомянуть: замыкание удерживает всё окружение, поэтому ссылка на большой объект внутри долгоживущей функции может стать утечкой памяти.
Итог
- Замыкание — функция плюс окружение, в котором она создана.
- Переменные внешней функции живут, пока на них ссылается внутренняя.
- Замыкания дают приватные данные, счётчики, мемоизацию и «настраиваемые» функции.