Разберите следующий JavaScript-кейс (вопрос на тонкости семантики): for (var i=0;iconsole.log(i),100); } — объясните вывод программы, почему он такой, и предложите по крайней мере три способа модифицировать код, чтобы он вывёл 0,1,2

30 Сен в 17:27
10 +2
0
Ответы
1

Вывод будет
3
3
3

Почему: переменная i объявлена через var — она имеет функцию/глобальную область видимости, не блоковую. В теле цикла вы создаёте три таймаута, каждый замыкает одну и ту же переменную i поссылкепо ссылкепоссылке. Таймауты выполнятся асинхронно, после завершения синхронного цикла for, а на тот момент i уже стало равным 3 послепоследнегоi++после последнего i++послепоследнегоi++. Поэтому все три колбэка прочитают текущее значение i = 3.

Как исправить чтобывывести0,1,2чтобы вывести 0, 1, 2чтобывывести0,1,2. Несколько способов:

1) Использовать let в заголовке цикла block−scopedпеременнаяblock-scoped переменнаяblockscopedпеременная:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}

let создаёт новую привязку i для каждой итерации, замыкания получают разные значения.

2) Закрыть текущее значение в IIFE старыйстильстарый стильстарыйстиль:
for (var i = 0; i < 3; i++) {
function(j)setTimeout(function()console.log(j);,100);function (j) {
setTimeout(function () { console.log(j); }, 100);
}
function(j)setTimeout(function()console.log(j);,100);
iii;
}

Здесь в каждую итерацию создаётся локальная j со значением i, и колбэк замыкает j.

3) Передать аргумент в setTimeout аргументыпослезадержкипередаютсявcallbackаргументы после задержки передаются в callbackаргументыпослезадержкипередаютсявcallback:
for (var i = 0; i < 3; i++) {
setTimeoutfunction(j)console.log(j);,100,ifunction (j) { console.log(j); }, 100, ifunction(j)console.log(j);,100,i;
}

Или с arrow-функцией:
for (var i = 0; i < 3; i++) {
setTimeout((j) => console.log(j), 100, i);
}

4) Скопировать значение в локальную let-переменную внутри тела цикла:
for (var i = 0; i < 3; i++) {
let j = i;
setTimeout(() => console.log(j), 100);
}

5) Использовать bind, чтобы «захватить» аргумент:
for (var i = 0; i < 3; i++) {
setTimeoutconsole.log.bind(null,i),100console.log.bind(null, i), 100console.log.bind(null,i),100;
}

Все эти варианты приведут к выводу 0, 1, 2 пооднойстрокенакаждыйтаймаутпо одной строке на каждый таймаутпооднойстрокенакаждыйтаймаут. Выбор способа зависит от целевой среды ES6+—простоlet;еслинуженобратныйуровеньсовместимости—IIFEES6+ — просто let; если нужен обратный уровень совместимости — IIFEES6+простоlet;еслинуженобратныйуровеньсовместимостиIIFE.

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