← Назад к вопросам
Определить вывод кода с замыканиями и циклом
1.7 Middle🔥 191 комментариев
#JavaScript Core
Условие
Определите, что выведет следующий код, и объясните почему.
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Задание
- Объясните, почему код выводит именно такой результат
- Предложите как минимум 3 способа исправить код, чтобы он выводил 0, 1, 2
Подсказка
Подумайте о разнице между var и let, а также о способах создания отдельной области видимости для каждой итерации.
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Эта классическая задача на понимание замыканий, области видимости (var vs let) и асинхронного выполнения JavaScript.
Что выведет код?
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Результат: 3 3 3
Почему 3, 3, 3?
- Цикл выполняется синхронно, переменная i увеличивается до 3
- setTimeout() выполняется асинхронно (через 1000мс)
- К моменту выполнения callback'ов цикл уже завершён, i = 3
- Все три callback'а ссылаются на одну переменную i в глобальной области видимости
- Поэтому все выводят 3
Способ 1: Использовать let вместо var
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Результат: 0 1 2
Почему работает:
- let создаёт новую область видимости для каждой итерации
- Каждый callback получает свою переменную i
- Это рекомендуемый современный подход
Способ 2: Замыкание через IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
// Результат: 0 1 2
Как работает:
- IIFE (Immediately Invoked Function Expression) создаёт новую область видимости
- Параметр j получает текущее значение i
- Каждое замыкание захватывает свой j
Способ 3: Замыкание через функцию-генератор
function createLogger(value) {
return function() {
console.log(value);
};
}
for (var i = 0; i < 3; i++) {
setTimeout(createLogger(i), 1000);
}
// Результат: 0 1 2
Как работает:
- createLogger() создаёт новое замыкание с уникальным value
- Каждое замыкание захватывает свой value
Способ 4: Использовать bind()
for (var i = 0; i < 3; i++) {
setTimeout((function(j) {
console.log(j);
}).bind(null, i), 1000);
}
// Результат: 0 1 2
Как работает:
- bind() создаёт новую функцию с привязанными параметрами
- Каждой функции привязывается текущее значение i
Способ 5: Arrow function с параметром
for (var i = 0; i < 3; i++) {
setTimeout((i) => {
console.log(i);
}, 1000, i);
}
// Результат: 0 1 2
Как работает:
- setTimeout принимает аргументы после задержки
- Arrow function получает параметр i
- Создаётся новое замыкание для каждой итерации
Способ 6: Массив и forEach
Array.from({length: 3}, (_, i) => {
setTimeout(function() {
console.log(i);
}, 1000);
});
// Результат: 0 1 2
Как работает:
- forEach создаёт новую область видимости для каждой итерации
- Параметр i автоматически локален
Сравнение решений
| Способ | Простота | Современность | Рекомендация |
|---|---|---|---|
| let | ✅✅ | ✅✅ | Лучше всего |
| IIFE | 📊 | ✅ | Legacy code |
| Функция | 📊 | ✅ | Functional |
| bind() | 📊 | ✅ | OK |
| Arrow + параметр | 📊 | ✅ | OK |
| forEach | ✅ | ✅ | Для массивов |
Key Concepts
- var — функциональная область видимости (function-scoped)
- let — блочная область видимости (block-scoped)
- Замыкание — функция, которая помнит переменные из внешней области
- setTimeout — асинхронный, выполняется после завершения синхронного кода
- Стек вызовов — цикл завершается ДО выполнения setTimeout callback'ов
Event Loop объяснение
Синхронный код:
for (var i = 0; i < 3; i++) { // i = 0, 1, 2, потом 3
setTimeout(...); // Добавляет в queue
}
// Цикл завершился, i = 3
Асинхронный код (через 1000мс):
console.log(i); // i = 3 (одна переменная для всех!)
console.log(i); // i = 3
console.log(i); // i = 3
Рекомендации для собеседования
- Объясните проблему с var и hoisting
- Покажите let как современное решение
- Объясните IIFE для лучшего понимания замыканий
- Обсудите Event Loop и асинхронность
- Покажите разницу var vs let vs const
Лучший ответ для production: Использовать let или const - это стандарт ES6, самый читаемый и безопасный.