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

В чем причина работы замыкания?

1.0 Junior🔥 231 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

В чем причина работы замыкания

Определение замыкания

Замыкание (closure) — это функция, которая имеет доступ к переменным из своей внешней функции (внешней области видимости) даже после того, как эта внешняя функция закончила выполнение. Замыкание работает благодаря лексической области видимости в JavaScript.

Причина работы замыкания: лексическая область видимости

Лексическая область видимости означает, что область видимости функции определяется местом, где она определена в коде, а не где она вызывается:

const globalVar = "global";

function outer() {
  const outerVar = "outer";

  function inner() {
    const innerVar = "inner";
    console.log(innerVar);    // "inner" - своя переменная
    console.log(outerVar);    // "outer" - переменная родительской функции
    console.log(globalVar);   // "global" - глобальная переменная
  }

  return inner;
}

const closureFunc = outer();
closureFunc(); // Работает! inner имеет доступ к outerVar

В этом примере функция inner имеет доступ к outerVar, потому что она определена внутри outer. Это не зависит от того, где вызывается closureFunc.

Механизм работы: цепочка областей видимости (Scope Chain)

Когда JavaScript интерпретатор выполняет функцию, он создает цепочку областей видимости:

function createCounter() {
  let count = 0; // Переменная сохраняется в памяти благодаря замыканию

  return function() {
    count++;
    return count;
  };
}

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

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

  1. createCounter() создает новую область видимости с переменной count
  2. Функция-счетчик определена внутри этой области видимости
  3. Когда createCounter() возвращает функцию, она запоминает свою внешнюю область
  4. JavaScript не удаляет область видимости createCounter() из памяти, потому что она все еще нужна
  5. Каждый вызов counter() имеет доступ к этой сохраненной переменной count

Причина 1: Область видимости сохраняется в памяти

Обычно, когда функция заканчивает выполнение, ее область видимости удаляется из памяти (garbage collection). Но если внутренняя функция ссылается на переменные внешней функции, JavaScript сохраняет эту область в памяти:

function makeMultiplier(multiplier) {
  // Эта область видимости сохранится в памяти благодаря замыканию
  return function(number) {
    return number * multiplier; // multiplier будет доступен
  };
}

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

console.log(double(5)); // 10
console.log(triple(5)); // 15

// Каждое замыкание имеет свою область видимости
// double запомнил multiplier = 2
// triple запомнил multiplier = 3

Причина 2: Функция сохраняет ссылку на внешние переменные

Когда функция определяется, она создает внутреннее свойство [[Scope]], которое содержит ссылку на все внешние области видимости:

function parent() {
  const parentVar = "parent";

  function child() {
    // child.[[Scope]] указывает на scope parent()
    console.log(parentVar);
  }

  return child;
}

const closure = parent();
closure(); // Работает! child все еще имеет доступ к parentVar

Практический пример: инкапсуляция данных

Замыкание часто используется для создания приватных переменных:

function createBankAccount(initialBalance) {
  // balance — приватная переменная, доступна только через замыкание
  let balance = initialBalance;

  return {
    deposit: function(amount) {
      balance += amount;
      return balance;
    },
    withdraw: function(amount) {
      balance -= amount;
      return balance;
    },
    getBalance: function() {
      return balance;
    }
  };
}

const account = createBankAccount(1000);
console.log(account.deposit(500));    // 1500
console.log(account.withdraw(200));   // 1300
console.log(account.getBalance());    // 1300

// Нельзя напрямую изменить balance
// account.balance = 999999; // Не сработает, balance приватная

Причина 3: Каррирование и частичное применение

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

function createLogger(logLevel) {
  // logLevel запоминается в замыкании
  return function(message) {
    console.log(`[${logLevel}] ${message}`);
  };
}

const infoLog = createLogger("INFO");
const errorLog = createLogger("ERROR");

infoLog("Application started");   // [INFO] Application started
errorLog("Something went wrong"); // [ERROR] Something went wrong

Визуализация памяти

// Когда создаем замыкание, переменные остаются в памяти
function createFunction() {
  const secret = "secret data"; // Остается в памяти!

  return function() {
    return secret;
  };
}

const func1 = createFunction(); // В памяти есть: func1 + secret
const func2 = createFunction(); // В памяти есть: func1 + secret + func2 + secret

// Каждое замыкание имеет свою копию переменных
console.log(func1()); // "secret data"
console.log(func2()); // "secret data"

Потенциальная проблема: утечка памяти

Когда замыкания удерживают большие объекты в памяти дольше, чем нужно:

const hugeData = new Array(1000000).fill("data"); // Большой объект

function createCallback() {
  return function() {
    // Замыкание сохраняет ссылку на hugeData
    console.log(hugeData.length);
  };
}

const callback = createCallback();
// hugeData не будет удален из памяти, пока существует callback

Заключение

Замыкание работает благодаря:

  1. Лексической области видимости — функция имеет доступ к переменным того места, где она определена
  2. Сохранению области видимости в памяти — JavaScript не удаляет область видимости, если на нее есть ссылки
  3. Цепочке областей видимости — каждая функция может обращаться к переменным из внешних функций

Это мощный механизм, который позволяет создавать инкапсуляцию данных, каррирование и другие полезные паттерны в JavaScript.

В чем причина работы замыкания? | PrepBro