0

Читаю книгу "You don't know JavaScript". Тема - замыкания.

for (var i=1; i<=5; i++) {
 setTimeout( function timer(){
  console.log( i );
 }, i*1000 );
}

Код выше, по словам автора, выведет 6 раз шестерку, ибо

Все функции обратного вызова функции timeout запускаются прямо после завершения цикла

Проблема была решена следующим образом:

for (var i=1; i<=5; i++) {
 (function(j){
  setTimeout( function timer(){
   console.log( j );
  }, j*1000 );
 })( i );
}

Аргумент анонимной функции j перенял ссылку на глобальное i. Это ок. Но вот следующее меня очень смущает:

for (let i=1; i<=5; i++) {
 setTimeout( function timer(){
  console.log( i );
 }, i*1000 );
}

Осознаю разницу между let и var. С let переменная i видима только для своего блока. В данном случае для цикла. В то время, как var глобален. С ним можно получить i вне блока. Но почему в последнем примере setTimeout не сработал после цикла, как было озвучено выше. Ведь вся разница с самым первым примером кода и последним в доступе i вне блока for. Я думал, что замыканием внутри анонимной функции и решается проблема запуска setTimeout после цикла, а не во время него. Так почему при условии глобального i setTimeout запустился после цикла, а при локальной i setTimeout запускается при каждой итерации цикла?

1 Answers1

1

setTimeout в любом случае асинхронный и всегда запустится позже цикла. Разница в том, что с let эта переменная не одна, а создается другая новая на каждый шаг цикла, и функция в таймауте каждый раз обращается к разным переменным, а не к одной изменяемой.

Цитата из https://learn.javascript.ru/let-const:

При использовании в цикле, для каждой итерации создаётся своя переменная.

Переменная var – одна на все итерации цикла и видна даже после цикла:

for(var i=0; i<10; i++) { /* … */ }

alert(i); // 10 С переменной let – всё по-другому.

Каждому повторению цикла соответствует своя независимая переменная let. Если внутри цикла есть вложенные объявления функций, то в замыкании каждой будет та переменная, которая была при соответствующей итерации.

  • То есть, в примере с var, цикл дойдет до конца и тогда всем setTimeout'ам раздастся одна-единственная переменная i, которая по завершению цикла будет равна 6. При let, на каждой итерации создается НОВАЯ i с другим значением и выдается одному setTimeout'у и отправляет его в стек. И так, пока цикл не закончится с этим пересозданием i и его раздачей каждому setTimeout'у. Правильно понял? –  Apr 05 '20 at 20:45
  • 1
    Всё правильно . –  Apr 05 '20 at 20:47