} Почему выводит 5 пять раз, как это связано с области видимости и замыканиями, и какие разные способы исправления (ES5/ES6/функциональные) вы предложите
Коротко — потому что цикл использует переменную с функциональной областью видимости (`var`), замыкания сохраняют ссылку на эту одну переменную, а к моменту выполнения отложенных функций её значение стало (5)(5)(5). То есть все замыкания читают одно и то же итоговое значение. Пример проблемы: ```js for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); // выведет 5 пять раз }, 0); } ``` Почему так происходит (пояснение): `var i` — одна переменная в функции; каждая функция в `setTimeout` замыкает ссылку на эту переменную, а не её текущее значение. Когда таймауты выполняются, цикл уже завершён и `i === 5`, поэтому все выводы — (5)(5)(5). Способы исправить 1) ES5 — IIFE (замкнуть текущее значение в параметре) ```js for (var i = 0; i < 5; i++) { (function (iCopy) { setTimeout(function () { console.log(iCopy); }, 0); })(i); } ``` 2) ES5 — bind или передача аргумента ```js for (var i = 0; i < 5; i++) { setTimeout(function (j) { console.log(j); }.bind(null, i), 0); } // или for (var i = 0; i < 5; i++) { setTimeout((function (j) { return function () { console.log(j); }; })(i), 0); } ``` 3) ES6 — блочная область видимости `let` (самый простой) ```js for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i), 0); // выведет 0,1,2,3,4 } ``` В ES6 `let` создаёт новое связывание для каждой итерации цикла, поэтому каждое замыкание видит своё значение. 4) Функциональный стиль — методы коллекций / фабрики ```js [0,1,2,3,4].forEach(function (i) { setTimeout(() => console.log(i), 0); }); // или фабрика колбэков function makeLogger(x) { return function () { console.log(x); }; } for (var i = 0; i < 5; i++) { setTimeout(makeLogger(i), 0); } ``` Краткая сводка: - Проблема — замыкания захватывают ссылку на одну `var`-переменную; результат меняется к моменту выполнения. Результат — повторный вывод (5)(5)(5). - Решения — создать отдельное лексическое связывание для каждой итерации (IIFE, `.bind`, фабрика) или использовать блоковую область `let`/`const` (ES6) или функциональные методы (`forEach`, фабрики).
Пример проблемы:
```js
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i); // выведет 5 пять раз
}, 0);
}
```
Почему так происходит (пояснение): `var i` — одна переменная в функции; каждая функция в `setTimeout` замыкает ссылку на эту переменную, а не её текущее значение. Когда таймауты выполняются, цикл уже завершён и `i === 5`, поэтому все выводы — (5)(5)(5).
Способы исправить
1) ES5 — IIFE (замкнуть текущее значение в параметре)
```js
for (var i = 0; i < 5; i++) {
(function (iCopy) {
setTimeout(function () {
console.log(iCopy);
}, 0);
})(i);
}
```
2) ES5 — bind или передача аргумента
```js
for (var i = 0; i < 5; i++) {
setTimeout(function (j) {
console.log(j);
}.bind(null, i), 0);
}
// или
for (var i = 0; i < 5; i++) {
setTimeout((function (j) {
return function () { console.log(j); };
})(i), 0);
}
```
3) ES6 — блочная область видимости `let` (самый простой)
```js
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0); // выведет 0,1,2,3,4
}
```
В ES6 `let` создаёт новое связывание для каждой итерации цикла, поэтому каждое замыкание видит своё значение.
4) Функциональный стиль — методы коллекций / фабрики
```js
[0,1,2,3,4].forEach(function (i) {
setTimeout(() => console.log(i), 0);
});
// или фабрика колбэков
function makeLogger(x) {
return function () { console.log(x); };
}
for (var i = 0; i < 5; i++) {
setTimeout(makeLogger(i), 0);
}
```
Краткая сводка:
- Проблема — замыкания захватывают ссылку на одну `var`-переменную; результат меняется к моменту выполнения. Результат — повторный вывод (5)(5)(5).
- Решения — создать отдельное лексическое связывание для каждой итерации (IIFE, `.bind`, фабрика) или использовать блоковую область `let`/`const` (ES6) или функциональные методы (`forEach`, фабрики).