> Как исправить проблему с 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: let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
// Решение 2: IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100); // 0, 1, 2
})(i);
}
// Решение 3: bind
for (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 создаёт новое связывание на каждой итерации - это ключевой момент.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью