Проблема и почему так выводится - В коде с `for (var i = 0; i console.log(i), 1000)` переменная `i` объявлена через `var`, она имеет функциональную (не блочную) область видимости. Колбэки, переданные в `setTimeout`, выполнятся примерно через 100010001000 мс, после того как цикл завершится, и все они читают одну и ту же переменную `i`, значение которой к тому моменту равно 555. Поэтому вывод будет пять раз 555. Способы исправить (чтобы вывести 0…40\ldots40…4) 1) Использовать `let` в заголовке цикла (блочная область для каждой итерации): ``` for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i), 1000); } ``` Объяснение: для каждой итерации создаётся своё `i`. 2) Захват значения через IIFE (немедленно вызываемая функция): ``` for (var i = 0; i < 5; i++) { (function (j) { setTimeout(() => console.log(j), 1000); })(i); } ``` Объяснение: `j` — параметр новой функции, фиксирует текущее значение `i` (копируется по значению). 3) Передать аргумент в `setTimeout` (поддерживается в браузерах/Node): ``` for (var i = 0; i < 5; i++) { setTimeout((j) => console.log(j), 1000, i); } ``` Объяснение: `setTimeout` сам вызовет функцию с переданным аргументом `i`. 4) Создать локальную переменную `let` внутри тела цикла: ``` for (var i = 0; i < 5; i++) { let j = i; setTimeout(() => console.log(j), 1000); } ``` 5) Использовать `Function.prototype.bind` для привязки аргумента: ``` for (var i = 0; i < 5; i++) { setTimeout(console.log.bind(null, i), 1000); } ``` Объяснение: `bind` создаёт функцию с уже зафиксированным значением аргумента. Разница между `var`, `let` и замыканиями (кратко) - `var`: функциональная область видимости или глобальная; одна и та же переменная доступна во всех итерациях цикла. При создании замыканий они захватывают ссылку на эту переменную, поэтому все функции увидят её финальное значение. - `let`: блочная область видимости; в заголовке `for` для каждой итерации создаётся отдельная привязка переменной, из-за чего замыкания видят своё собственное значение. - Замыкания: функции «замыкают» переменные окружения (ссылку на переменную). Если нужно зафиксировать текущее значение, его надо скопировать в новую область (через параметр функции, `let`-переменную и т.п.).
- В коде с `for (var i = 0; i console.log(i), 1000)` переменная `i` объявлена через `var`, она имеет функциональную (не блочную) область видимости. Колбэки, переданные в `setTimeout`, выполнятся примерно через 100010001000 мс, после того как цикл завершится, и все они читают одну и ту же переменную `i`, значение которой к тому моменту равно 555. Поэтому вывод будет пять раз 555.
Способы исправить (чтобы вывести 0…40\ldots40…4)
1) Использовать `let` в заголовке цикла (блочная область для каждой итерации):
```
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
```
Объяснение: для каждой итерации создаётся своё `i`.
2) Захват значения через IIFE (немедленно вызываемая функция):
```
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(() => console.log(j), 1000);
})(i);
}
```
Объяснение: `j` — параметр новой функции, фиксирует текущее значение `i` (копируется по значению).
3) Передать аргумент в `setTimeout` (поддерживается в браузерах/Node):
```
for (var i = 0; i < 5; i++) {
setTimeout((j) => console.log(j), 1000, i);
}
```
Объяснение: `setTimeout` сам вызовет функцию с переданным аргументом `i`.
4) Создать локальную переменную `let` внутри тела цикла:
```
for (var i = 0; i < 5; i++) {
let j = i;
setTimeout(() => console.log(j), 1000);
}
```
5) Использовать `Function.prototype.bind` для привязки аргумента:
```
for (var i = 0; i < 5; i++) {
setTimeout(console.log.bind(null, i), 1000);
}
```
Объяснение: `bind` создаёт функцию с уже зафиксированным значением аргумента.
Разница между `var`, `let` и замыканиями (кратко)
- `var`: функциональная область видимости или глобальная; одна и та же переменная доступна во всех итерациях цикла. При создании замыканий они захватывают ссылку на эту переменную, поэтому все функции увидят её финальное значение.
- `let`: блочная область видимости; в заголовке `for` для каждой итерации создаётся отдельная привязка переменной, из-за чего замыкания видят своё собственное значение.
- Замыкания: функции «замыкают» переменные окружения (ссылку на переменную). Если нужно зафиксировать текущее значение, его надо скопировать в новую область (через параметр функции, `let`-переменную и т.п.).