Кейс на JavaScript: for (var i=0;iconsole.log(i),100); } — почему выводит 5 пять раз, как это исправить с помощью различных приёмов (ES5/ES6), и обсудите влияние замыканий
Коротко — причина и решения. Почему выводит 555 пять раз - В вашем коде используется `var`, а оно даёт одну переменную `i` в функции (function-scoped). Таймауты запускаются асинхронно позже (после ~100100100 мс), к тому времени цикл уже завершён и `i === 555`. Все колбэки замкнулись на одну и ту же переменную `i`, поэтому печатается текущее (последнее) значение 555. Решения 1) ES6 — блоковая область видимости `let` for (let i=000; i<555; i++) { setTimeout(() => console.log(i), 100100100); } Объяснение: `let` создаёт новую привязку `i` для каждой итерации, замыкание захватывает её копию. 2) ES5 — IIFE (замыкание через параметр) for (var i=000; i<555; i++) { (function(j) { setTimeout(function() { console.log(j); }, 100100100); })(i); } Объяснение: IIFE получает текущее значение `i` в параметр `j`, каждое замыкание хранит своё число. 3) ES5 — bind (или создание функции с аргументом) for (var i=000; i<555; i++) { setTimeout((function(j){ return function(){ console.log(j); }; })(i), 100100100); } или с bind: for (var i=000; i<555; i++) { setTimeout(function(j){ console.log(j); }.bind(null, i), 100100100); } 4) Использовать аргументы setTimeout (поддерживается в большинстве сред) for (var i=000; i<555; i++) { setTimeout(function(j) { console.log(j); }, 100100100, i); } 5) Создать локальную копию внутри цикла (ES6/ES5+) for (var i=000; i<555; i++) { const j = i; // или var j = i; внутри IIFE setTimeout(() => console.log(j), 100100100); } Коротко о замыканиях - Замыкание — это функция, которая "помнит" ссылку на переменные внешней области видимости. При `var` все колбэки ссылаются на одну переменную `i`, поэтому видят её итоговое значение. При `let` или при создании новой переменной/параметра (IIFE, bind) для каждой итерации создаётся своя привязка — замыкание получит нужное значение. Если нужно — покажу минимальные рабочие примеры или объясню отличия в поведении в разных средах.
Почему выводит 555 пять раз
- В вашем коде используется `var`, а оно даёт одну переменную `i` в функции (function-scoped). Таймауты запускаются асинхронно позже (после ~100100100 мс), к тому времени цикл уже завершён и `i === 555`. Все колбэки замкнулись на одну и ту же переменную `i`, поэтому печатается текущее (последнее) значение 555.
Решения
1) ES6 — блоковая область видимости `let`
for (let i=000; i<555; i++) {
setTimeout(() => console.log(i), 100100100);
}
Объяснение: `let` создаёт новую привязку `i` для каждой итерации, замыкание захватывает её копию.
2) ES5 — IIFE (замыкание через параметр)
for (var i=000; i<555; i++) {
(function(j) {
setTimeout(function() { console.log(j); }, 100100100);
})(i);
}
Объяснение: IIFE получает текущее значение `i` в параметр `j`, каждое замыкание хранит своё число.
3) ES5 — bind (или создание функции с аргументом)
for (var i=000; i<555; i++) {
setTimeout((function(j){ return function(){ console.log(j); }; })(i), 100100100);
}
или с bind:
for (var i=000; i<555; i++) {
setTimeout(function(j){ console.log(j); }.bind(null, i), 100100100);
}
4) Использовать аргументы setTimeout (поддерживается в большинстве сред)
for (var i=000; i<555; i++) {
setTimeout(function(j) { console.log(j); }, 100100100, i);
}
5) Создать локальную копию внутри цикла (ES6/ES5+)
for (var i=000; i<555; i++) {
const j = i; // или var j = i; внутри IIFE
setTimeout(() => console.log(j), 100100100);
}
Коротко о замыканиях
- Замыкание — это функция, которая "помнит" ссылку на переменные внешней области видимости. При `var` все колбэки ссылаются на одну переменную `i`, поэтому видят её итоговое значение. При `let` или при создании новой переменной/параметра (IIFE, bind) для каждой итерации создаётся своя привязка — замыкание получит нужное значение.
Если нужно — покажу минимальные рабочие примеры или объясню отличия в поведении в разных средах.