Разберите следующий JavaScript‑кейс: for (var i = 0; i console.log(i), 0); } Почему он печатает 3,3,3 и какие варианты исправления (без изменения логики цикла) вы предложите, объяснив механизм каждого решения?

16 Дек 2025 в 11:09
16 +1
0
Ответы
1
Коротко — потому что переменная iii объявлена через `var` (функциональная/глобальная область видимости), колбэки захватывают не текущее значение, а ссылку на одну и ту же переменную iii. Таймауты выполняются после окончания цикла, когда iii стало 333, поэтому все три вывода — 3,3,33,3,33,3,3.
Пример временной шкалы:
- шаги цикла: i=0,1,2i=0,1,2i=0,1,2 — для каждого создаётся отложенный колбэк;
- цикл завершён, iii стало 333;
- выполняются отложенные функции — каждая читает текущее iii (= 333).
Варианты исправления (не меняя «логики цикла» — т.е. не трогая порядок итераций):
1) Замкнуть значение через IIFE (немедленно вызываемая функция):
for (var i = 0; i < 3; i++) {
(function(current){
setTimeout(function(){ console.log(current); }, 0);
})(i);
}
Механизм: IIFE получает параметр `current` — это новая локальная переменная, замыкаемая колбэком, фиксирующая значение на момент вызова.
2) Создать локальную блочную копию через `let` внутри тела:
for (var i = 0; i < 3; i++) {
let j = i;
setTimeout(() => console.log(j), 0);
}
Механизм: `j` — блочная переменная, для каждой итерации создаётся своя копия, которую захватывает колбэк.
3) Использовать фабрику функций:
for (var i = 0; i < 3; i++) {
function makeLogger(x){ return () => console.log(x); }
setTimeout(makeLogger(i), 0);
}
Механизм: `makeLogger(i)` возвращает замыкание над фиксированным `x`.
4) Использовать `Function.prototype.bind`:
for (var i = 0; i < 3; i++) {
setTimeout(console.log.bind(null, i), 0);
}
Механизм: `bind` создаёт новую функцию с уже привязанным аргументом (значение берётся при вызове `bind`).
5) Передать аргумент в `setTimeout` (поддерживается в среде браузера/Node):
for (var i = 0; i < 3; i++) {
setTimeout(function(x){ console.log(x); }, 0, i);
}
или
for (var i = 0; i < 3; i++) {
setTimeout(console.log, 0, i);
}
Механизм: дополнительные аргументы к `setTimeout` фиксируются при планировании и передаются в колбэк при выполнении.
(Доп. простой вариант — заменить `var` на `let` в заголовке цикла:
for (let i = 0; i console.log(i), 0); }
— это меняет область видимости переменной: для каждой итерации создаётся своё `i`.)
Выбор варианта — по стилю и окружению; наиболее идиоматичны сейчас `let` в заголовке цикла или создание блочной копии `let j = i`.
16 Дек 2025 в 11:17
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир