Замыкания: понятие и практическое применение
Что такое замыкание? 🤔
Замыкание (closure) — это комбинация функции и лексического окружения, в котором эта функция была объявлена. Другими словами, функция «запоминает» переменные из своей родительской области видимости, даже после того как родительская функция завершила работу.
function outer() {
let counter = 0;
function inner() {
counter++;
console.log(counter);
}
return inner;
}
const myClosure = outer();
myClosure(); // 1
myClosure(); // 2
Здесь inner помнит переменную counter и может изменять её, даже после завершения outer.
Как работают замыкания? 🧠
Когда функция создаётся, она получает доступ не только к своим локальным переменным, но и к переменным внешних функций (если они есть). Это возможно благодаря лексической области видимости, которая определяет доступность переменных на этапе объявления функции, а не её вызова.
function createGreeting(greeting) {
return function(name) {
console.log(`${greeting}, ${name}!`);
};
}
const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");
sayHello("Alice"); // "Hello, Alice!"
sayHi("Bob"); // "Hi, Bob!"
Функции sayHello и sayHi сохраняют значения greeting благодаря замыканию.
Зачем нужны замыкания? 💡
-
Хранение состояния между вызовами
Например, создание счётчиков или кэширующих функций. -
Сокрытие данных (инкапсуляция)
Замыкания позволяют создавать приватные переменные. -
Функциональное программирование
Каррирование, мемоизация, фабричные функции.
Практические примеры 🔧
1. Счётчик с замыканием
function createCounter() {
let count = 0;
return {
increment() { count++; },
decrement() { count--; },
getCount() { return count; }
};
}
const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 2
Здесь count скрыт от внешнего мира и доступен только через методы.
2. Мемоизация (кэширование)
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
console.log("Returning cached result");
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
const slowCalculation = (x) => x * 2;
const fastCalculation = memoize(slowCalculation);
console.log(fastCalculation(5)); // 10 (вычислено)
console.log(fastCalculation(5)); // 10 (из кэша)
3. Каррирование
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(3)); // 6
Ловушки замыканий ⚠️
1. Циклы и замыкания
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
} // Выведет 3, 3, 3
Решение: используйте let (блочная область видимости) или IIFE.
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
} // Выведет 0, 1, 2
2. Утечки памяти
Замыкания могут сохранять ссылки на большие объекты даже после завершения работы.
function heavyFunction() {
const bigData = new Array(1000000).fill("data");
return function() {
console.log("I have access to bigData!");
};
}
const holdMemory = heavyFunction();
// bigData остаётся в памяти, пока существует holdMemory
Совет: освобождайте ссылки на ненужные данные, если они больше не нужны.
Итог 🎯
Замыкания — мощный инструмент, который:
- ✅ Позволяет сохранять состояние.
- ✅ Создаёт приватные переменные.
- ✅ Используется в каррировании, мемоизации и модульном коде.
Попробуйте применить их в реальных проектах — например, для управления состоянием или оптимизации вычислений! 🚀