JavaScript & TypeScriptМодуль 2: Функции
Замыкания
Как функции запоминают переменные
Цель урока
В этом уроке ты научишься:
- Понимать, что такое замыкание
- Использовать замыкания на практике
- Избегать типичных ошибок
Что такое замыкание
Замыкание — это функция, которая запоминает переменные из места, где была создана, даже после того, как внешняя функция завершилась.
function createGreeter(greeting) {
// greeting "замыкается" во внутренней функции
return function(name) {
console.log(greeting + ", " + name + "!");
};
}
const sayHello = createGreeter("Привет");
const sayBye = createGreeter("Пока");
sayHello("Иван"); // "Привет, Иван!"
sayBye("Мария"); // "Пока, Мария!"Как это работает
function outer() {
let count = 0; // эта переменная "живёт" в замыкании
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3Ключевой момент
Каждый вызов outer() создаёт новое замыкание со своей переменной count.
const counter1 = outer();
const counter2 = outer();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 — отдельный счётчикПрактические примеры
Приватные переменные
function createBankAccount(initialBalance) {
let balance = initialBalance; // приватная переменная
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) {
return "Недостаточно средств";
}
balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.getBalance()); // 1000
console.log(account.deposit(500)); // 1500
console.log(account.withdraw(200)); // 1300
// account.balance — undefined (недоступна напрямую)Фабрика функций
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15Мемоизация
function memoize(fn) {
const cache = {};
return function(arg) {
if (cache[arg] !== undefined) {
console.log("Из кэша");
return cache[arg];
}
console.log("Вычисляем");
const result = fn(arg);
cache[arg] = result;
return result;
};
}
const factorial = memoize(function(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
});
console.log(factorial(5)); // Вычисляем → 120
console.log(factorial(5)); // Из кэша → 120Замыкание в цикле
Проблема
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
// 3, 3, 3 (не 0, 1, 2!)Решение 1: let
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
// 0, 1, 2Решение 2: IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 100);
})(i);
}
// 0, 1, 2Итого
- Замыкание — функция + её лексическое окружение
- Позволяет создавать приватные переменные
- Каждый вызов внешней функции создаёт новое замыкание
- Используй
letв циклах для избежания проблем
Практика
Задание 1: Счётчик
Задача: Создай функцию makeCounter, которая возвращает объект с методами increment, decrement, getValue.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
0 1 2 1
Задание 2: Фабрика функций
Задача: Создай функцию createAdder(n), которая возвращает функцию, прибавляющую n.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
15 25
Задание 3: Однократный вызов
Задача: Создай функцию once(fn), которая позволяет вызвать fn только один раз.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
Инициализация undefined
Проверь себя
- Что такое замыкание?
- Почему
varв цикле сsetTimeoutвыводит одно значение? - Как создать приватную переменную?