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

«Что такое замыкания (Closures) в JavaScript?» — вопрос из категории JavaScript, который задают на 35% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Замыкание (Closure) — это функция, которая «запоминает» лексическое окружение (переменные, параметры, другие функции), в котором она была создана, даже после того, как это внешнее окружение завершило выполнение. Это фундаментальная концепция JavaScript, активно используемая в Node.js.

Простой пример:

function createCounter() {
  let count = 0; // `count` — переменная в лексическом окружении createCounter

  // Функция increment является замыканием
  return function increment() {
    count += 1; // Она имеет доступ к `count` даже после завершения createCounter
    return count;
  };
}

const myCounter = createCounter(); // createCounter завершилась, но её окружение «живёт» в замыкании
console.log(myCounter()); // 1
console.log(myCounter()); // 2
console.log(myCounter()); // 3
// У каждого вызова createCounter() — своё независимое замыкание с отдельной `count`.

Практическое применение в Node.js:

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

    function createDatabaseConnection(config) {
      const connectionString = config.url; // «Приватная» переменная
      return {
        connect() { console.log(`Connecting to ${connectionString}`); },
        query(sql) { /* использует connectionString */ }
      };
    }
    const db = createDatabaseConnection({ url: 'localhost:5432' });
    // Нет прямого доступа к `connectionString` извне, только через методы.
  2. Фабрики и middleware: Многие библиотеки Express используют замыкания для создания конфигурируемых middleware.

    function logger(format) {
      return function(req, res, next) { // Middleware — замыкание, помнящее `format`
        console.log(format, req.method, req.url);
        next();
      };
    }
    app.use(logger(':method :url'));

Важный нюанс: Замыкание хранит ссылку на переменную, а не её значение на момент создания. Это может приводить к классической ошибке в циклах с асинхронными операциями, которую решают с помощью let (блочная область видимости) или дополнительного замыкания.