Рассмотрите JavaScript-кейс: for (var i=0;iconsole.log(i),100); } — объясните, какой будет вывод, почему так происходит и как изменить код, чтобы напечатать 0–4
Вывод будет: 555555555555555. Почему: - Переменная `i` объявлена через `var`, она имеет функцию/глобальную область видимости, одна общая переменная для всех итераций. - Цикл выполняется синхронно; все `setTimeout` только планируют колбэки, а сами колбэки запускаются позже (после ~100 мс), когда цикл уже завершён и значение `i` стало 5\,55. - Замыкания внутри стрелочных функций берут ссылку на ту же самую переменную `i`, поэтому каждый колбэк читает текущее (конечное) значение 5\,55. Как исправить (печать 000–444) — несколько вариантов: 1) Использовать `let` (блочная область видимости, для каждой итерации своё `i`): for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i), 100); } Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4. 2) Замыкание через IIFE (сохраняем текущее значение в аргумент): for (var i = 0; i < 5; i++) { (function(j) { setTimeout(() => console.log(j), 100); })(i); } Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4. 3) Передать значение как аргумент в `setTimeout` (поддерживается в браузерах/Node): for (var i = 0; i < 5; i++) { setTimeout((j) => console.log(j), 100, i); } Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4. 4) Использовать `Function.prototype.bind`: for (var i = 0; i < 5; i++) { setTimeout(console.log.bind(null, i), 100); } Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4. Любой из этих способов решает проблему захвата переменной и даст последовательный вывод 000–444.
555 555 555 555 555.
Почему:
- Переменная `i` объявлена через `var`, она имеет функцию/глобальную область видимости, одна общая переменная для всех итераций.
- Цикл выполняется синхронно; все `setTimeout` только планируют колбэки, а сами колбэки запускаются позже (после ~100 мс), когда цикл уже завершён и значение `i` стало 5\,55.
- Замыкания внутри стрелочных функций берут ссылку на ту же самую переменную `i`, поэтому каждый колбэк читает текущее (конечное) значение 5\,55.
Как исправить (печать 000–444) — несколько вариантов:
1) Использовать `let` (блочная область видимости, для каждой итерации своё `i`):
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100);
}
Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4.
2) Замыкание через IIFE (сохраняем текущее значение в аргумент):
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i);
}
Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4.
3) Передать значение как аргумент в `setTimeout` (поддерживается в браузерах/Node):
for (var i = 0; i < 5; i++) {
setTimeout((j) => console.log(j), 100, i);
}
Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4.
4) Использовать `Function.prototype.bind`:
for (var i = 0; i < 5; i++) {
setTimeout(console.log.bind(null, i), 100);
}
Вывод: 0,1,2,3,40,1,2,3,40,1,2,3,4.
Любой из этих способов решает проблему захвата переменной и даст последовательный вывод 000–444.