Проанализируйте следующий JavaScript-кусок: for (var i = 0; i

12 Окт в 14:18
2 +1
0
Ответы
1

Результат этого кода — три строки с числом 3 вконсолибудетнапечатанопоочереди3,3,3в консоли будет напечатано по очереди 3, 3, 3вконсолибудетнапечатанопоочереди3,3,3.

Почему так происходит короткокороткокоротко:

var i — функциональная неблочнаяне блочнаянеблочная переменная. В цикле создаётся одна переменная i, которую все три функции в setTimeout «замыкают» онихранятссылкунаоднуитужепеременнуюони хранят ссылку на одну и ту же переменнуюонихранятссылкунаоднуитужепеременную.setTimeout с задержкой 100 ms выполняет колбэки уже после завершения цикла. К тому моменту цикл закончился и i стало равно 3. Поэтому каждая из функций при вызове читает текущее значение i = 3.

Пояснение с точки зрения событийного цикла: setTimeout планирует колбэк в очередь событий; выполнение колбэков произойдёт позже, когда стек свободен — а к тому времени переменная i уже изменилась.

Три разных способа исправить спримерами,плюспреимущества/недостаткис примерами, плюс преимущества/недостаткиспримерами,плюспреимущества/недостатки:

1) Использовать let ES6ES6ES6, блочная область видимости

for (let i = 0; i < 3; i++) {
setTimeoutfunction()console.log(i);,100function() {
console.log(i);
}, 100
function()console.log(i);,100
;
}

Почему работает: при каждой итерации для i создаётся новая блочная привязка, колбэк «замыкает» своё собственное i. Выведет 0, 1, 2.

Плюсы: лаконично, современно, понятное поведение.
Минусы: требует ES6 встарыхокруженияхбезтранспиляции—несовместимов старых окружениях без транспиляции — несовместимовстарыхокруженияхбезтранспиляциинесовместимо.

2) Создать замыкание через IIFE подходитдлястарогоJSподходит для старого JSподходитдлястарогоJS

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;
}

Почему работает: IIFE получает текущее значение i в параметр j и создаёт новую область, которую замыкает колбэк. Выведет 0, 1, 2.

Плюсы: работает в старых средах без ES6.
Минусы: более многословно, может выглядеть менее современно.

3) Передать значение в setTimeout илииспользоватьbindили использовать bindилииспользоватьbind Вариант A — дополнительные аргументы setTimeout:

for (var i = 0; i < 3; i++) {
setTimeoutfunction(j)console.log(j);,100,ifunction(j) {
console.log(j);
}, 100, i
function(j)console.log(j);,100,i
;
}

Вариант B — bind:

for (var i = 0; i < 3; i++) {
setTimeoutconsole.log.bind(null,i),100console.log.bind(null, i), 100console.log.bind(null,i),100;
}

Почему работает: setTimeout позволяет передать аргументы в колбэк илиbindфиксируетаргументили bind фиксирует аргументилиbindфиксируетаргумент — передаётся текущее значение i, которое становится независимым от переменной i. Выведет 0, 1, 2.

Плюсы: коротко, явно фиксирует значение.
Минусы: передача дополнительных аргументов setTimeout исторически поддерживается большинством сред, но может выглядеть необычно; bind может менять контекст/параметры внекоторыхслучаяхнеподходит,еслинуженспецифичныйthisв некоторых случаях не подходит, если нужен специфичный thisвнекоторыхслучаяхнеподходит,еслинуженспецифичныйthis.

Короткая заметка: аналогично let сработает и const вfor−of/forциклеconstдаётновуюпривязкунакаждуюитерацию,нозначениенеможетбытьпереназначеновтелеитерациив for-of/for цикле const даёт новую привязку на каждую итерацию, но значение не может быть переназначено в теле итерациивforof/forциклеconstдаётновуюпривязкунакаждуюитерацию,нозначениенеможетбытьпереназначеновтелеитерации.

Вывод: в современных проектах самый простой и читаемый способ — использовать let илиconstили constилиconst, в старых — IIFE или передачу аргументов/ bind.

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