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