Почему следующий JavaScript-код "for (var i = 0; i console.log(i), 0); }" выводит три раза число 3, как объяснить поведение с точки зрения лексической области видимости и замыканий, и как исправить код, чтобы вывести 0,1,2
Давайте разберемся, почему ваш код for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } выводит трижды число 3.
Причина поведения
Лексическая область видимости: В JavaScript, переменная, объявленная с помощью var, имеет функциональную область видимости, а не блочную. Это значит, что переменная i в вашем цикле for доступна и сохраняется в замыкании для всех функций, которые вы создаете внутри цикла, таких как функция обратного вызова setTimeout.
Замыкание: Когда функция обратного вызова, созданная через setTimeout, выполняется, она обращается не к значению i на момент вызова, а к самой переменной i, которая находится в верхнем контексте тоестьвscopeцикла<code>for</code>то есть в scope цикла <code>for</code>тоестьвscopeцикла<code>for</code>. По окончании цикла значение i будет равно 3, так как цикл завершился. Таким образом, при выполнении всех функций обратного вызова setTimeout будет выведено значение i, которое на тот момент равно 3.
Как исправить код
Для корректного результата вывода0,1и2вывода 0, 1 и 2вывода0,1и2 существует несколько способов. Один из них - использование блока let вместо var, так как let имеет блочную область видимости.
Вот исправленный код:
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }
Теперь каждый вызов setTimeout будет замыкаться на индивидуальную переменную i каждой итерации цикла, и в результате вы получите ожидаемый вывод:
0 1 2Альтернативный способ: передача аргумента
Если по каким-то причинам вы не можете использовать let, можно передать текущее значение i как аргумент в функцию:
for (var i = 0; i < 3; i++) { setTimeout((i) => console.log(i), 0, i); }
В этом случае, функция обратного вызова будет получать текущее значение i, и в результате вы тоже получите вывод:
0 1 2
Таким образом, оба предложенных исправления обеспечивают желаемый результат, используя разные подходы.
Давайте разберемся, почему ваш код for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } выводит трижды число 3.
Причина поведенияЛексическая область видимости: В JavaScript, переменная, объявленная с помощью var, имеет функциональную область видимости, а не блочную. Это значит, что переменная i в вашем цикле for доступна и сохраняется в замыкании для всех функций, которые вы создаете внутри цикла, таких как функция обратного вызова setTimeout.
Замыкание: Когда функция обратного вызова, созданная через setTimeout, выполняется, она обращается не к значению i на момент вызова, а к самой переменной i, которая находится в верхнем контексте тоестьвscopeцикла<code>for</code>то есть в scope цикла <code>for</code>тоестьвscopeцикла<code>for</code>. По окончании цикла значение i будет равно 3, так как цикл завершился. Таким образом, при выполнении всех функций обратного вызова setTimeout будет выведено значение i, которое на тот момент равно 3.
Как исправить кодДля корректного результата вывода0,1и2вывода 0, 1 и 2вывода0,1и2 существует несколько способов. Один из них - использование блока let вместо var, так как let имеет блочную область видимости.
Вот исправленный код:
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0);
}
Теперь каждый вызов setTimeout будет замыкаться на индивидуальную переменную i каждой итерации цикла, и в результате вы получите ожидаемый вывод:
01
2Альтернативный способ: передача аргумента
Если по каким-то причинам вы не можете использовать let, можно передать текущее значение i как аргумент в функцию:
for (var i = 0; i < 3; i++) {setTimeout((i) => console.log(i), 0, i);
}
В этом случае, функция обратного вызова будет получать текущее значение i, и в результате вы тоже получите вывод:
01
2
Таким образом, оба предложенных исправления обеспечивают желаемый результат, используя разные подходы.