Вопрос проверяет понимание механизма замыканий в JavaScript и особенностей захвата ссылочных типов, что важно для избежания ошибок в асинхронном коде и циклах.
Замыкание в JavaScript — это функция, которая запоминает свою внешнюю область видимости, даже после того, как внешняя функция завершила выполнение. Однако, если замыкание захватывает переменную ссылочного типа (например, объект, массив или переменную, объявленную через var), оно сохраняет не копию значения, а ссылку на саму переменную. Это означает, что любые изменения этой переменной после создания замыкания будут видны при вызове замыкания.
Классический пример — использование var в цикле for. Переменная i объявляется один раз и переиспользуется на каждой итерации. Все замыкания, созданные внутри цикла, ссылаются на одну и ту же переменную i. Когда замыкания выполняются (например, после завершения цикла), они видят последнее значение i.
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // выведет 3 три раза
}, 100);
}В этом примере каждое замыкание внутри setTimeout захватывает переменную i по ссылке. К моменту выполнения таймеров цикл уже завершился, и i равна 3. Поэтому все три вызова выводят 3.
Использование let в цикле создаёт новую переменную для каждой итерации, поэтому замыкание захватывает уникальное значение.
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // выведет 0, 1, 2
}, 100);
}Другой способ — создать новую область видимости с помощью немедленно вызываемой функции (IIFE), передавая текущее значение как аргумент.
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // выведет 0, 1, 2
}, 100);
})(i);
}Аналогичная проблема возникает при захвате объектов. Если замыкание использует объект, который позже мутируется, то при вызове замыкания оно увидит изменённое состояние.
let obj = { value: 1 };
const closure = () => console.log(obj.value);
obj.value = 2;
closure(); // выведет 2, а не 1Это поведение может быть как полезным (например, для реактивности), так и источником ошибок, если разработчик ожидает, что замыкание запомнит значение на момент создания.
Понимание того, что замыкания захватывают ссылки, а не значения, критично для написания предсказуемого кода. Особенно это важно при работе с асинхронными операциями, циклами и обработчиками событий. Используйте let, const или создавайте новые области видимости, чтобы избежать неожиданного поведения.
Frontend developer
Ментор по Frontend
Полное сопровождение до оффера — без дорогих курсов, с оплатой после трудоустройства
Записаться на консультацию