Замыкания
Вы уже знаете область видимости (scope): отсюда какие имена видны. Шаг дальше: внутренняя функция может пользоваться внешним именем даже после окончания внешнего вызова. Эта устойчивая связь — замыкание (closure).
Что значит замыкание
Замыкание возникает, когда функция создаётся внутри другой и позже всё ещё достигает привязок внешней. Внешний вызов может закончиться; внутренняя функция всё равно «помнит» эти имена. Слово closure в код не пишут — пишут вложенные функции, а JavaScript держит нужные внешние привязки живыми.
Три части под наблюдение
📊 Внешняя функция, привязка и внутренняя функция
| Часть | Роль | Подсказка новичку |
|---|---|---|
| внешняя функция | выполняется раз (или больше) и часто возвращает внутреннюю | вызов «настройки» |
| внешняя привязка | параметр или let / const во внешней |
значение, которое внутренней ещё нужно |
| внутренняя функция | использует эту привязку при позднем вызове | функция, которую вы сохраняете |
Если можете указать эти три, объясните почти любой учебный пример замыкания. Сюрприз не во вложенности — а в том, что внешняя привязка полезна после внешнего return.
function makeLine(title) {
function show() {
console.log(title);
}
show();
}
makeLine("Shop");
Во время вызова внутренняя смотрит наружу
У show нет локального title. Поиск идёт наружу к makeLine и находит параметр. Это обычная вложенная область. Замыкания идут дальше: сохраните show (или её двойника) на потом, когда makeLine уже вернулась.
function makeLine(title) {
return function show() {
console.log(title);
};
}
let printShop = makeLine("Shop");
printShop();
Внешний вызов закончился — память осталась
makeLine("Shop") возвращает функцию. Когда позже вызываете printShop, она всё ещё печатает Shop. Параметр title из того законченного вызова ещё доступен. Это устойчивый захват и есть замыкание.
function makeGreeter(name) {
return function () {
return "Hello, " + name;
};
}
let hiSara = makeGreeter("Sara");
console.log(hiSara());
Фабрики возвращают готовую функцию
makeGreeter — маленькая фабрика: один внешний вызов, одна настроенная функция обратно. hiSara всегда приветствует Сару, потому что замкнулась на этом name. Другой аргумент makeGreeter — другой приветствующий, тот же шаблон, новая память.
function makeCounter() {
let n = 0;
return function () {
n = n + 1;
return n;
};
}
let tick = makeCounter();
console.log(tick());
console.log(tick());
Почти приватное состояние со счётчиком
n живёт во внешней функции. Внешний код не трогает n напрямую — только возвращённая функция. Первый вызов даёт 1, второй 2. Счётчик «помнит» без глобальной переменной.
function makeCounter() {
let n = 0;
return function () {
n = n + 1;
return n;
};
}
let a = makeCounter();
let b = makeCounter();
console.log(a());
console.log(a());
console.log(b());
У каждого внешнего вызова своя память
a и b пришли из разных вызовов makeCounter(), значит у каждого свой n. Два шага на a не заставляют b начать с середины. Замыкания не делят мешок состояния сами по себе, если вы намеренно не делите одну внешнюю привязку.
Показать замкнутое значение на странице
Соберите приветствие фабрикой, вызовите один раз и положите возвращённую строку в абзац.
<!DOCTYPE html>
<html>
<head>
<title>closures — page</title>
</head>
<body>
<p id="msg">…</p>
<script>
function makeGreeter(name) {
return function () {
return "Hello, " + name;
};
}
let hi = makeGreeter("Omar");
let box = document.getElementById("msg");
box.textContent = hi();
</script>
</body>
</html>
Абзац показывает Hello, Omar. Внешний вызов закончился; hi всё ещё знает name. Смените только строку для makeGreeter, обновите, прочитайте новую строку. Дальше: объекты — связанные значения под одним именем.
Практика
- Напишите функцию, которая возвращает внутреннюю и логирует внешний параметр.
- Сохраните возвращённую функцию и вызовите после окончания внешнего вызова.
- Соберите
makeGreeter("Lina")и залогируйте возвращённую строку. - Создайте один счётчик; вызовите дважды и убедитесь в
1, затем2. - Создайте два счётчика; увеличьте только первый дважды и проверьте, что второй стартует с
1. - В HTML-демо смените только аргумент имени, обновите, прочитайте абзац.
Частые ошибки
- Думать, что внешние локальные исчезают в момент
return— внутренняя ещё может их видеть - Ожидать, что каждый
makeCounter()делит одинn— обычно у каждого вызова своя память - Считать, что «замыкание» — это только слово
return—returnможет отдать внутреннюю, память — замкнутая привязка - Класть каждое запоминаемое значение на глобальный объект «для простоты»
- Ждать здесь полный урок по объектам — группировка свойств под одним значением на следующей странице
Кратко
- Замыкание позволяет внутренней функции использовать внешние имена после конца внешнего вызова
- Вы пишете вложенные функции; JavaScript держит нужные привязки живыми
- Фабрики возвращают настроенную функцию, помнящую значения настройки
- Счётчики прячут приватный
nза возвращённой функцией - Отдельные внешние вызовы обычно значат отдельные памяти
- Обновление страницы может показать строку из замкнутого имени
- Дальше: объекты — связанные значения под одним именем
🧠 Проверьте Свои Знания
Проверьте Свои Знания
Бросьте себе вызов с помощью этой интерактивной викторины и узнайте, насколько хорошо вы понимаете тему
📝 Инструкции
- Внимательно прочитайте каждый вопрос
- Выберите лучший ответ на каждый вопрос
- Вы можете пересдавать тест столько раз, сколько захотите
- Ваш прогресс будет показан вверху