Загрузка...

Замыкания

Вы уже знаете область видимости (scope): отсюда какие имена видны. Шаг дальше: внутренняя функция может пользоваться внешним именем даже после окончания внешнего вызова. Эта устойчивая связь — замыкание (closure).

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

Замыкание возникает, когда функция создаётся внутри другой и позже всё ещё достигает привязок внешней. Внешний вызов может закончиться; внутренняя функция всё равно «помнит» эти имена. Слово closure в код не пишут — пишут вложенные функции, а JavaScript держит нужные внешние привязки живыми.

Три части под наблюдение

📊 Внешняя функция, привязка и внутренняя функция

Часть Роль Подсказка новичку
внешняя функция выполняется раз (или больше) и часто возвращает внутреннюю вызов «настройки»
внешняя привязка параметр или let / const во внешней значение, которое внутренней ещё нужно
внутренняя функция использует эту привязку при позднем вызове функция, которую вы сохраняете

Если можете указать эти три, объясните почти любой учебный пример замыкания. Сюрприз не во вложенности — а в том, что внешняя привязка полезна после внешнего return.

Внутренняя функция читает внешнее имя

javascript
function makeLine(title) {
function show() {
console.log(title);
}
show();
}
makeLine("Shop");

Во время вызова внутренняя смотрит наружу

У show нет локального title. Поиск идёт наружу к makeLine и находит параметр. Это обычная вложенная область. Замыкания идут дальше: сохраните show (или её двойника) на потом, когда makeLine уже вернулась.

Сохранить внутреннюю функцию после return

javascript
function makeLine(title) {
return function show() {
console.log(title);
};
}
let printShop = makeLine("Shop");
printShop();

Внешний вызов закончился — память осталась

makeLine("Shop") возвращает функцию. Когда позже вызываете printShop, она всё ещё печатает Shop. Параметр title из того законченного вызова ещё доступен. Это устойчивый захват и есть замыкание.

Маленькая фабрика приветствия

javascript
function makeGreeter(name) {
return function () {
return "Hello, " + name;
};
}
let hiSara = makeGreeter("Sara");
console.log(hiSara());

Фабрики возвращают готовую функцию

makeGreeter — маленькая фабрика: один внешний вызов, одна настроенная функция обратно. hiSara всегда приветствует Сару, потому что замкнулась на этом name. Другой аргумент makeGreeter — другой приветствующий, тот же шаблон, новая память.

Счётчик, который помнит

javascript
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. Счётчик «помнит» без глобальной переменной.

Два счётчика, две памяти

javascript
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 начать с середины. Замыкания не делят мешок состояния сами по себе, если вы намеренно не делите одну внешнюю привязку.

Показать замкнутое значение на странице

Соберите приветствие фабрикой, вызовите один раз и положите возвращённую строку в абзац.

Демо страницы с замыканием

html
<!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, обновите, прочитайте новую строку. Дальше: объекты — связанные значения под одним именем.

Практика

  1. Напишите функцию, которая возвращает внутреннюю и логирует внешний параметр.
  2. Сохраните возвращённую функцию и вызовите после окончания внешнего вызова.
  3. Соберите makeGreeter("Lina") и залогируйте возвращённую строку.
  4. Создайте один счётчик; вызовите дважды и убедитесь в 1, затем 2.
  5. Создайте два счётчика; увеличьте только первый дважды и проверьте, что второй стартует с 1.
  6. В HTML-демо смените только аргумент имени, обновите, прочитайте абзац.

Частые ошибки

  • Думать, что внешние локальные исчезают в момент return — внутренняя ещё может их видеть
  • Ожидать, что каждый makeCounter() делит один n — обычно у каждого вызова своя память
  • Считать, что «замыкание» — это только слово return — return может отдать внутреннюю, память — замкнутая привязка
  • Класть каждое запоминаемое значение на глобальный объект «для простоты»
  • Ждать здесь полный урок по объектам — группировка свойств под одним значением на следующей странице

Кратко

  • Замыкание позволяет внутренней функции использовать внешние имена после конца внешнего вызова
  • Вы пишете вложенные функции; JavaScript держит нужные привязки живыми
  • Фабрики возвращают настроенную функцию, помнящую значения настройки
  • Счётчики прячут приватный n за возвращённой функцией
  • Отдельные внешние вызовы обычно значат отдельные памяти
  • Обновление страницы может показать строку из замкнутого имени
  • Дальше: объекты — связанные значения под одним именем

🧠 Проверьте Свои Знания

Готов к Началу

Проверьте Свои Знания

Бросьте себе вызов с помощью этой интерактивной викторины и узнайте, насколько хорошо вы понимаете тему

❓
8
Вопросы
🎯
70%
Для Прохождения
♾️
∞
Время
🔄
∞
Попытки

📝 Инструкции

  • Внимательно прочитайте каждый вопрос
  • Выберите лучший ответ на каждый вопрос
  • Вы можете пересдавать тест столько раз, сколько захотите
  • Ваш прогресс будет показан вверху