Замыкания (Closures) — это одна из самых мощных и одновременно запутанных концепций в JavaScript. Простыми словами, замыкание — это функция, которая запоминает переменные из места своего создания, даже если она вызывается в другом месте. Это открывает возможности для инкапсуляции, но может приводить к утечкам памяти.
Как работает область видимости
Когда функция создается внутри другой функции, она имеет доступ к переменным внешней функции. Это связывание сохраняется даже после того, как внешняя функция завершила выполнение.
function createCounter() {
let count = 0; // Приватная переменная
return {
increment: () => count++,
decrement: () => count--,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
console.log(counter.getCount()); // 1
// Переменная count недоступна извне напрямую
В этом примере count защищен от прямого изменения из глобальной области видимости. Это паттерн модуля, реализованный через замыкание.
Замыкания в циклах: классическая проблема
До появления let разработчики часто сталкивались с проблемой замыканий в циклах var. Переменная var имеет функциональную область видимости, а не блочную.
// Ошибка с var
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // Выведет 3, 3, 3
}, 100);
}
// Решение с let
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // Выведет 0, 1, 2
}, 100);
}
Использование let создает новую блочную область видимости для каждой итерации цикла, поэтому замыкание «захватывает» уникальное значение i для каждого таймера.
Память и утечки
Замыкания хранят ссылки на переменные внешней области. Если замыкание живет долго (например, висит в глобальном объекте или слушателе событий), оно удерживает в памяти все переменные, которые были доступны в момент его создания.
function setup() {
const hugeData = new Array(1000000).fill('*');
document.getElementById('btn').addEventListener('click', () => {
console.log('Click');
// hugeData удерживается в памяти, хотя не используется
});
}
Чтобы избежать утечек, обнуляйте ссылки на большие объекты, если они не нужны внутри замыкания, или удаляйте слушатели событий, когда компонент уничтожается.