JavaScript

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

Замыкание (Closure)

Замыкание - это функция, которая помнит переменные из внешней области видимости даже после того, как эта область завершила выполнение.

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

В JavaScript каждая функция при создании «захватывает» ссылку на лексическое окружение, в котором она была объявлена. Это окружение хранит переменные и остаётся живым, пока на него есть хотя бы одна ссылка.

function makeCounter() {
  let count = 0          // переменная во внешней функции
  return function() {
    count++              // внутренняя функция «помнит» count
    return count
  }
}

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

После вызова makeCounter() её стек-фрейм уничтожается, но переменная count остаётся живой, потому что возвращённая функция держит на неё ссылку.

Практическое применение

Инкапсуляция и приватные переменные:

function createUser(name) {
  let _score = 0          // «приватное» поле

  return {
    getName: () => name,
    addScore: (n) => { _score += n },
    getScore: () => _score,
  }
}

const user = createUser("Алиса")
user.addScore(10)
console.log(user.getScore()) // 10
// _score недоступна снаружи напрямую

Частичное применение (partial application):

function multiply(a) {
  return (b) => a * b
}

const double = multiply(2)
const triple = multiply(3)
console.log(double(5))  // 10
console.log(triple(5))  // 15

Классическая ловушка: замыкание в цикле

// ❌ Проблема - все кнопки выведут 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100)
}

// ✅ Решение 1: let создаёт новую переменную на каждую итерацию
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100)
}

// ✅ Решение 2: IIFE создаёт новое замыкание
for (var i = 0; i < 3; i++) {
  ;(function(j) {
    setTimeout(() => console.log(j), 100)
  })(i)
}

Утечки памяти

Замыкания могут случайно удерживать большие объекты в памяти. Если вы замкнули ссылку на DOM-элемент или большой массив, а сама функция живёт долго - сборщик мусора (GC, Garbage Collector) не сможет освободить память. Решение - присвоить null после использования.

Резюме

  • Замыкание создаётся каждый раз, когда объявляется функция внутри другой функции.
  • Функция хранит ссылку на переменную, а не её копию - поэтому изменения видны.
  • Используется для инкапсуляции, каррирования, мемоизации, обработчиков событий.

Источники