← Назад к вопросам

Что такое замыкание (closure) в JavaScript и для чего оно используется?

1.3 Junior🔥 252 комментариев
#JavaScript Core

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое замыкание (closure) в JavaScript?

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

Механизм работы замыкания

Рассмотрим классический пример:

function createCounter() {
  let count = 0; // переменная в лексическом окружении createCounter
  
  return function() {
    count += 1; // внутренняя функция использует переменную count
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

В этом примере:

  1. Функция createCounter создает локальную переменную count.
  2. Она возвращает внутреннюю функцию, которая увеличивает и возвращает count.
  3. После выполнения createCounter её локальная переменная count, казалось бы, должна быть уничтожена. Однако возвращенная функция (на которую ссылается counter) сохраняет ссылки на своё лексическое окружение, включая count. Это окружение продолжает существовать, так как на него есть активная ссылка из функции counter.

Для чего используется замыкание?

Замыкания применяются во множестве сценариев, благодаря своей способности сохранять состояние и контролировать доступ к переменным.

1. Создание приватных переменных и инкапсуляция

В JavaScript до появления классов (ES6) замыкания были основным способом создания приватного состояния.

function createPrivateStorage() {
  let privateData = {};
  
  return {
    set: function(key, value) {
      privateData[key] = value;
    },
    get: function(key) {
      return privateData[key];
    },
    delete: function(key) {
      delete privateData[key];
    }
  };
}

const storage = createPrivateStorage();
storage.set('secret', '12345');
console.log(storage.get('secret')); // '12345'
console.log(storage.privateData); // undefined - напрямую недоступно!

Здесь переменная privateData недоступна извне, но методы объекта могут её изменять благодаря замыканию.

2. Создание функций с сохранением состояния

Замыкания идеально подходят для создания функций, которые "помнят" свое предыдущее состояние — как в примере со счетчиком выше.

3. Реализация callback'ов и обработчиков событий

В обработчиках событий часто нужно сохранить контекст или данные.

function setupButton(color) {
  const button = document.createElement('button');
  button.textContent = 'Click me';
  
  button.addEventListener('click', function() {
    // Эта функция-обработчик сохраняет доступ к параметру color
    console.log(`Button color is: ${color}`);
  });
  
  return button;
}

const redButton = setupButton('red');
// При клике на redButton выведется "Button color is: red"

4. Мемоизация и оптимизация

Замыкания позволяют создавать кэширующие функции для оптимизации повторных вычислений.

function createMemoizedFunction(fn) {
  const cache = {};
  
  return function(arg) {
    if (cache[arg]) {
      console.log('Returning cached result');
      return cache[arg];
    }
    
    console.log('Computing new result');
    const result = fn(arg);
    cache[arg] = result;
    return result;
  };
}

const expensiveCalculation = createMemoizedFunction(function(n) {
  // Предположим, здесь сложные вычисления
  return n * n;
});

console.log(expensiveCalculation(5)); // Computing new result → 25
console.log(expensiveCalculation(5)); // Returning cached result → 25

5. Функциональное программирование и создание фабрик функций

Замыкания позволяют создавать специализированные функции на основе параметров.

function createMultiplier(multiplier) {
  return function(value) {
    return value * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(10)); // 20
console.log(triple(10)); // 30

Важные особенности и практические советы

  • Замыкание сохраняет ссылки на переменные, а не на их значения. Если переменная изменяется, замыкание будет использовать её текущее значение.
  • Неумышленные замыкания могут приводить к утечке памяти. Если замыкание сохраняет ссылки на большие объекты, которые уже не нужны, они не будут освобождены сборщиком мусора.
  • В циклах создание замыканий требует внимательности. Частая ошибка — использование переменной цикла внутри замыкания без учёта её финального значения.
// Проблемный код
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // Всегда выводит 3!
  }, times[100]);
}

// Решение с помощью замыкания
for (var i = 0; i < 3; i++) {
  setTimeout(function(index) {
    return function() {
      console.log(index); // Выводит 0, 1, 2
    };
  }(i), times[100]);
}

Итог

Замыкания — это мощный механизм JavaScript, который позволяет функциям сохранять доступ к своему лексическому окружению. Они являются ключевым инструментом для:

  • Инкапсуляции и создания приватного состояния
  • Сохранения контекста в асинхронных операциях и событиях
  • Оптимизации через мемоизацию
  • Создания фабрик и специализированных функций

Понимание замыканий критически важно для написания эффективного, безопасного и модульного кода в JavaScript, особенно в современных приложениях с сложной state-логикой и компонентной архитектурой.