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

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

📌 Техническое определение

Замыкание — это комбинация:

  1. Функции (обычно вложенной),

  2. Ссылок на лексическое окружение (scope), в котором она была определена.

Иными словами, функция «захватывает» (или «закрывает» — отсюда название) переменные из внешней области видимости, и может использовать их позже, даже если внешняя функция уже завершила выполнение.

📘 Пример на JavaScript

function outer() {
let counter = 0;
return function inner() {
counter++;
return counter;
};
}
const count = outer();
console.log(count()); // 1
console.log(count()); // 2
console.log(count()); // 3

Здесь:

  • inner() — это замыкание.

  • Оно захватывает переменную counter из области видимости outer.

  • Даже после того, как outer() завершила выполнение, переменная counter остаётся живой в замыкании.

📘 Пример на Python

def outer()
counter = 0
def inner():
nonlocal counter
counter += 1
return counter
return inner
f = outer()
print(f()) # 1
print(f()) # 2

Здесь:

  • inner — замыкание, использующее counter из outer.

  • nonlocal позволяет изменять переменную из внешней (не глобальной) области.

📘 Пример на Dart

Function makeCounter() {
int count = 0;
return () {
count++;
return count;
};
}
void main() {
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
}

🔬 Как это работает под капотом

Когда функция создаётся, она получает ссылку на лексическое окружение (lexical environment), в котором она была определена. Это окружение хранится вместе с функцией. Когда функция позже вызывается, она использует это сохранённое окружение.

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

📚 Применения замыканий

Инкапсуляция состояния
Можно скрыть переменные от внешнего доступа, предоставляя к ним доступ только через функции.

<br/>function secretPassword() {
const password = '123456';
return () => password;
}
const getPassword = secretPassword();
console.log(getPassword()); // '123456'

Фабрики функций (Function Factories)
Можно создавать функции с предзаданными параметрами.

<br/>function multiplyBy(x) {
return function(y) {
return x \* y;
};
}
const double = multiplyBy(2);
console.log(double(5)); // 10

Обработка событий и колбэки
В JS-обработчиках событий замыкания сохраняют доступ к переменным вне слушателя.

<br/>for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
}
// 0, 1, 2  правильно, потому что let создаёт замыкание на каждом шаге цикла

Мемоизация (Memoization)
Замыкания позволяют хранить кэш промежуточных значений:

<br/>function memoize(fn) {
const cache = {};
return function(x) {
if (x in cache) return cache\[x\];
return cache\[x\] = fn(x);
};
}
const square = memoize(x => x \* x);

⚠️ Важные особенности

  • Переменные "захватываются по ссылке", а не копируются.

  • Если замыкание использует переменную, и она изменяется где-то позже — замыкание будет видеть новое значение.

  • Это может привести к ловушкам при асинхронном программировании или в циклах с var.

📌 Отличие от просто вложенных функций

Не каждая вложенная функция — это замыкание. Только та, которая использует переменные из внешней функции после её завершения, становится замыканием.

function outer() {
let x = 10;
function inner() {
console.log("Hello");
}
return inner;
}

Здесь inner — вложенная функция, но не замыкание, так как не использует переменные из outer.

🧠 Замыкания в других языках

  • Python: замыкания реализуются через nonlocal и лексическое окружение.

  • Swift: замыкания (closures) — полноценные объекты-функции, захватывающие переменные.

  • Dart: поддерживает замыкания, аналогично JS.

  • C++: лямбды с захватом [=], [&] создают замыкания.

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