← Назад к вопросам
В чём разница между HTTP 1.0, 1.1, 2.0?
2.3 Middle🔥 171 комментариев
#Сети и протоколы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Closures в JavaScript: Полное объяснение
Closure (замыкание) — это функция, которая имеет доступ к переменным из внешней области видимости (scope), даже после того как внешняя функция завершила работу. Это одна из самых важных концепций в JavaScript.
Основной принцип
// Простейший пример
function outer() {
const message = 'Hello'; // переменная outer scope
function inner() {
console.log(message); // inner имеет доступ к message
}
return inner;
}
const greet = outer();
greet(); // "Hello"
// Даже после того как outer() закончила работу,
// inner всё ещё имеет доступ к message
Это и есть closure: inner функция «закрыла» переменную message в себе.
Как это работает?
Scope Chain (цепь областей видимости)
Каждая функция в JavaScript имеет доступ к:
- Своим локальным переменным
- Переменным родительской функции
- Глобальным переменным
const global = 'I am global';
function level1() {
const l1 = 'Level 1';
function level2() {
const l2 = 'Level 2';
function level3() {
console.log(l2); // Доступ к level2 scope
console.log(l1); // Доступ к level1 scope
console.log(global); // Доступ к global scope
}
return level3;
}
return level2();
}
const fn = level1();
fn(); // Всё работает!
Практические примеры
1. Data Privacy — приватные данные
// Без closure
class BankAccount {
balance = 1000; // Публичная! Можно изменить
}
const account = new BankAccount();
account.balance = 999999; // Упс!
// С closure
function createBankAccount(initialBalance) {
let balance = initialBalance; // Приватная переменная
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) throw new Error('Insufficient funds');
balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(1000);
account.deposit(500); // 1500
account.withdraw(200); // 1300
// balance напрямую не может быть изменена!
console.log(account.balance); // undefined
2. Function Factories — создание функций
// Factory для создания функций с разными параметрами
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
const tenTimes = createMultiplier(10);
console.log(double(5)); // 10
console.log(triple(5)); // 15
console.log(tenTimes(5)); // 50
// Каждая функция «помнит» свой multiplier
3. Callbacks и Event Handlers
// Общая проблема: потеря контекста
const buttons = document.querySelectorAll('button');
buttons.forEach((button, index) => {
button.addEventListener('click', function() {
console.log(`Button ${index} clicked`); // index сохраняется в closure
});
});
// Без closure (var проблема):
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
console.log(`Button ${i} clicked`); // Всегда выведет последний i!
});
}
// С closure (правильно):
for (let i = 0; i < buttons.length; i++) { // let создаёт closure для каждой итерации
buttons[i].addEventListener('click', function() {
console.log(`Button ${i} clicked`); // Правильное i для каждой кнопки
});
}
4. Декораторы и Higher-Order Functions
// HOF: функция, которая возвращает функцию
function withLogging(fn) {
return function(...args) {
console.log(`Calling ${fn.name} with:`, args);
const result = fn(...args);
console.log(`Result:`, result);
return result;
};
}
function add(a, b) {
return a + b;
}
const addWithLogging = withLogging(add);
addWithLogging(2, 3); // Logs всё
// Практический пример: кэширование
function memoize(fn) {
const cache = {}; // Приватный кэш благодаря closure
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) {
console.log('Cache hit!');
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
function expensive(n) {
return n * n * n;
}
const memoizedExpensive = memoize(expensive);
console.log(memoizedExpensive(5)); // Вычисляет
console.log(memoizedExpensive(5)); // Cache hit!
5. Module Pattern
const Calculator = (function() {
// Приватные переменные и методы
let history = [];
function logOperation(operation, a, b, result) {
history.push({ operation, a, b, result });
}
// Публичный API
return {
add(a, b) {
const result = a + b;
logOperation('add', a, b, result);
return result;
},
subtract(a, b) {
const result = a - b;
logOperation('subtract', a, b, result);
return result;
},
getHistory() {
return history;
}
};
})();
Calculator.add(5, 3); // Логирует
Calculator.subtract(10, 4); // Логирует
console.log(Calculator.getHistory()); // Видишь историю
// history недоступна напрямую! Это приватное
6. Async операции с сохранением контекста
function fetchUser(userId) {
const startTime = Date.now();
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(data => {
const duration = Date.now() - startTime; // closure на startTime
console.log(`Loaded user ${userId} in ${duration}ms`);
return data;
});
}
// Или с async/await
async function fetchUserAsync(userId) {
const startTime = Date.now(); // closure на startTime
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
const duration = Date.now() - startTime;
console.log(`Loaded user ${userId} in ${duration}ms`);
return data;
}
Потенциальные проблемы
Memory Leaks через closures
// ❌ Проблема: большой объект остаётся в памяти
function createProcessor(hugeData) {
return function processSmallPart(index) {
return hugeData[index]; // Весь hugeData в памяти!
};
}
// ✅ Решение: сохраняй только нужное
function createProcessor(hugeData) {
const processed = hugeData.filter(/* ... */);
hugeData = null; // Очистить
return function processSmallPart(index) {
return processed[index];
};
}
Замыкание переменных в циклах (var vs let)
// ❌ var: все функции ссылаются на одну i
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// ✅ let: каждая функция имеет свою i (block scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
// ✅ Или функция-обёртка
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100); // 0, 1, 2
})(i);
}
Ключевые моменты
- Closure создаётся автоматически при создании функции
- Closure сохраняет переменные из родительской области видимости
- Closure позволяет создавать приватные переменные
- Closure используется в callbacks, HOF, модулях, декораторах
- Нужно следить за memory leaks при хранении больших объектов
- let/const безопаснее чем var при использовании closures