Ниже фрагмент JavaScript — объясните, почему код выводит именно такие значения, какие механизмы языка участвовали, и предложите две разные корректные версии, сохраняющие замыкание по каждой итерации цикла:
for (var i = 0; i

3 Окт в 14:37
13 +1
0
Ответы
1

Что выводит код
for (var i = 0; i < 3; i++) {
setTimeout(function(){ console.log(i); }, 100)
}
и почему

На практике этот код выведет:
3
3
3
(три раза число 3).

Почему так происходит (ключевые механизмы JS):

var — функциональная (или глобальная) область видимости. Переменная i одна на всю функцию (нет отдельной переменной для каждой итерации).setTimeout — асинхронная: колбэки ставятся в очередь и выполняются позже (после завершения текущного кода / цикла). То есть все три setTimeout регистрируются, цикл доходит до конца и i становится 3 до того, как любой из колбэков выполнится.Замыкание (closure) захватывает ссылку на переменную i, а не «значение в момент создания». Когда колбэки выполняются, они читают текущее значение i — это 3.

Две правильные версии, которые сохраняют отдельное замыкание на каждую итерацию

1) Использовать let (ES6) — для for каждая итерация получает свою лексическую привязку i:
for (let i = 0; i < 3; i++) {
setTimeout(function () { console.log(i); }, 100);
}
Вывод: 0, 1, 2. Причина: let создаёт новую привязку переменной для каждой итерации цикла, замыкание захватывает именно эту привязку.

2) Использовать IIFE (немедленно вызываемое функцию) и передавать i как параметр — захватить значение в локальной переменной функции:
for (var i = 0; i < 3; i++) {
(function (j) {
setTimeout(function () { console.log(j); }, 100);
})(i);
}
Вывод: 0, 1, 2. Причина: IIFE получает текущее значение i в параметре j; замыкание внутри setTimeout ссылается на j, уникальную для каждой итерации.

(Ещё альтернативы: использовать Function.prototype.bind: setTimeout(console.log.bind(null, i), 100), либо передавать аргументы в setTimeout: setTimeout(function(x){ console.log(x); }, 100, i).)

Кратко: проблема связана с областями видимости (var) и тем, что замыкание хранит ссылку на переменную, а колбэки выполняются позже. Решения — создать для каждой итерации отдельную переменную (let, IIFE, bind, аргументы setTimeout и т. п.).

3 Окт в 15:10
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир