> Как исправить проблему с var в цикле с setTimeout в JavaScript (JavaScript)
Уровень: middle · Роль: frontend · Категория: Технические вопросы
Компании: Aston
Стек: JavaScript
> Пример ответа
Короткий ответ
Проблема возникает из-за того, что var имеет функциональную область видимости, а не блочную. Все итерации цикла используют одну и ту же переменную, и к моменту вызова setTimeout она уже равна конечному значению. Решение - использовать let вместо var, обернуть тело цикла в IIFE или использовать bind для передачи значения как аргумента.
Подробное объяснение
В JavaScript var объявляет переменную в области видимости функции, а не блока. В цикле for (var i = 0; i < 3; i++) переменная i создаётся один раз и переиспользуется на каждой итерации. setTimeout - асинхронная операция, колбэк выполняется после завершения цикла. К этому моменту i уже равна конечному значению (например, 3), поэтому все колбэки выводят одно и то же число.
let создаёт новое связывание для каждой итерации цикла, поэтому каждый колбэк "захватывает" своё значение. Это работает благодаря тому, что let имеет блочную область видимости, и спецификация ECMAScript гарантирует создание отдельной переменной на каждой итерации.
Альтернативные подходы:
- IIFE (Immediately Invoked Function Expression) - создаёт новую область видимости для каждой итерации.
Function.prototype.bind- передаёт значение как аргумент в колбэк.- Замыкание через дополнительную функцию.
На практике
На собеседовании важно не только назвать решение, но и объяснить, почему оно работает. Покажите понимание event loop, замыканий и областей видимости. Начните с воспроизведения проблемы, затем предложите исправление через let, а потом упомяните альтернативы - это покажет глубину знаний.
Пример кода
JAVASCRIPT// Проблемаfor (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100); // 3, 3, 3}// Решение 1: letfor (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100); // 0, 1, 2}// Решение 2: IIFEfor (var i = 0; i < 3; i++) {(function(j) {setTimeout(() => console.log(j), 100); // 0, 1, 2})(i);}// Решение 3: bindfor (var i = 0; i < 3; i++) {setTimeout(console.log.bind(null, i), 100); // 0, 1, 2}
Как отвечать на собеседовании
Структурируйте ответ: сначала покажите проблему, затем объясните причину (область видимости var и асинхронность), потом дайте решение через let и кратко упомяните альтернативы. Не останавливайтесь только на "замените var на let" - объясните механику. Если спросят про const - уточните, что он тоже работает, но только если переменная не переназначается.
Что проверяет интервьюер
- Понимание областей видимости в JavaScript.
- Знание разницы между
var,let,const. - Понимание асинхронности и event loop.
- Умение работать с замыканиями.
- Способность объяснить не только "как", но и "почему".
Типичные ошибки
- Предложение заменить
varнаconstбез объяснения -constработает, но кандидат не понимает разницу сlet. - Использование
setTimeoutс0вместо100- не влияет на суть проблемы, но показывает непонимание. - Утверждение, что проблема в самом
setTimeout, а не в области видимости. - Предложение использовать
async/await- не решает проблему, так как колбэк всё равно выполняется после цикла. - Забывают упомянуть, что
letсоздаёт новое связывание на каждой итерации - это ключевой момент.
> Похожие задачи по frontend
Почему Promise.race не подходит для выбора лучшего покупателя в JavaScript
Как написать полифил для метода map в JavaScript
Что происходит при рекурсивном вызове с созданием промисов в JavaScript: зависнет ли страница или будет пропускаться рендер
Можно ли повесить обработчик сабмита на кнопку и на форму одновременно
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью