Встречается на 46% собеседований по JavaScript

Что такое замыкание

Замыкание — это функция, которая запоминает своё лексическое окружение (область видимости, в которой она была создана) и сохраняет доступ к его переменным даже после того, как эта область завершила выполнение. Проще говоря, функция «замыкает» переменные, которые были в её области видимости на момент создания, и может использовать их позже, даже если вызывается из другого контекста.

Как это работает

В JavaScript функции — это объекты, и каждая функция при создании получает скрытое свойство [[Environment]], которое ссылается на текущее лексическое окружение. Когда функция вызывается, интерпретатор создаёт новое окружение выполнения, но цепочка областей видимости строится на основе [[Environment]]. Поэтому функция всегда имеет доступ к переменным из внешней области, даже если внешняя функция уже вернула результат.

Ключевой момент: замыкание образуется не в момент вызова, а в момент объявления функции. Оно фиксирует не значения переменных, а сами переменные (по ссылке). Это значит, что если внешняя переменная изменится после создания замыкания, функция увидит новое значение.

Пример

Классический пример — счётчик:

javascript
function создатьСчетчик() {
    let количество = 0; // переменная замкнута внутри функции увеличить

    function увеличить() {
        количество += 1;
        return количество;
    }

    return увеличить;
}

const счетчик = создатьСчетчик();
console.log(счетчик()); // 1
console.log(счетчик()); // 2

Здесь увеличить продолжает видеть количество, хотя создатьСчетчик уже завершилась. Каждый вызов счетчик() изменяет одну и ту же переменную, поэтому состояние сохраняется между вызовами.

Другой пример — приватные переменные:

javascript
function создатьПользователя(имя) {
    let возраст = 0;
    return {
        получитьИмя: () => имя,
        установитьВозраст: (новыйВозраст) => { возраст = новыйВозраст; },
        получитьВозраст: () => возраст
    };
}

const юзер = создатьПользователя('Анна');
юзер.установитьВозраст(25);
console.log(юзер.получитьВозраст()); // 25
// переменная возраст недоступна напрямую извне

Подводные камни

  • Замыкание хранит ссылку, а не значение. Если внешняя переменная меняется, замыкание видит новое значение. Это часто приводит к ошибкам в циклах, например, при использовании var в for.
  • Память. Замыкание удерживает переменные в памяти до тех пор, пока сама функция жива. Если замыкание хранится долго, это может привести к утечкам памяти, особенно при работе с большими объектами.
  • Потеря контекста this. Замыкание не сохраняет this внешней функции. Если внутри замыкания нужен доступ к this, его нужно сохранить заранее (например, в переменную self) или использовать стрелочную функцию.

Когда использовать

Замыкания применяются для:

  • инкапсуляции состояния и создания приватных переменных;
  • фабрик функций, которые возвращают настроенные обработчики;
  • каррирования и частичного применения аргументов;
  • реализации мемоизации (кэширования результатов);
  • создания модулей, скрывающих внутренние детали.

Коротко

  • Замыкание — функция, которая сохраняет доступ к переменным своей лексической области видимости после её завершения.
  • Механизм основан на скрытом свойстве [[Environment]], которое хранит ссылку на внешнее окружение.
  • Замыкание хранит ссылку на переменные, а не их значения, поэтому изменения внешней переменной видны внутри функции.
  • Используется для инкапсуляции, сохранения состояния, каррирования и создания модулей; осторожно с памятью и this.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы