Коротко — потому что в исходном фрагменте переменная `i` объявлена через `var` (функциональная область видимости), все три функции в `setTimeout` захватывают одну и ту же переменную `i`. Цикл завершается до выполнения таймаутов, `i` становится равной ......... (то есть .........), поэтому каждая функция выводит это значение, а не свои ожидаемые ........., ........., .......... Механизмы, дающие такое поведение: - Замыкание: функция захватывает ссылку на переменную, а не её текущее значение. - Область видимости `var`: одна переменная `i` для всех итераций. - Event loop / макрозадачи: колбэки `setTimeout` выполняются асинхронно после завершения синхронного цикла. Как исправить, сохранив логику (несколько простых вариантов): 1) Использовать `let` в заголовке цикла (блоковая область видимости — для каждой итерации своя `i`): ```js for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 10); } ``` Ожидаемый вывод: `0`, `1`, `2`. 2) Закрыть текущее значение в IIFE (поддерживает старые окружения без `let`): ```js for (var i = 0; i < 3; i++) { (function(j) { setTimeout(function() { console.log(j); }, 10); })(i); } ``` 3) Передать аргументы в `setTimeout` (в современных средах поддерживается): ```js for (var i = 0; i < 3; i++) { setTimeout(function(j) { console.log(j); }, 10, i); } ``` 4) Создать фабрику функций: ```js function makeLogger(x) { return function() { console.log(x); }; } for (var i = 0; i < 3; i++) { setTimeout(makeLogger(i), 10); } ``` Любой из этих подходов сохраняет асинхронную логику, но гарантирует, что каждая функция получит своё значение итерации (........., ........., .........).
Механизмы, дающие такое поведение:
- Замыкание: функция захватывает ссылку на переменную, а не её текущее значение.
- Область видимости `var`: одна переменная `i` для всех итераций.
- Event loop / макрозадачи: колбэки `setTimeout` выполняются асинхронно после завершения синхронного цикла.
Как исправить, сохранив логику (несколько простых вариантов):
1) Использовать `let` в заголовке цикла (блоковая область видимости — для каждой итерации своя `i`):
```js
for (let i = 0; i < 3; i++) {
setTimeout(function() { console.log(i); }, 10);
}
```
Ожидаемый вывод: `0`, `1`, `2`.
2) Закрыть текущее значение в IIFE (поддерживает старые окружения без `let`):
```js
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() { console.log(j); }, 10);
})(i);
}
```
3) Передать аргументы в `setTimeout` (в современных средах поддерживается):
```js
for (var i = 0; i < 3; i++) {
setTimeout(function(j) { console.log(j); }, 10, i);
}
```
4) Создать фабрику функций:
```js
function makeLogger(x) {
return function() { console.log(x); };
}
for (var i = 0; i < 3; i++) {
setTimeout(makeLogger(i), 10);
}
```
Любой из этих подходов сохраняет асинхронную логику, но гарантирует, что каждая функция получит своё значение итерации (........., ........., .........).