setTimeout中的閉包

我們直接舉栗子說問題。

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

結果:開始輸出一個 5,然後每隔一秒再輸出一個 5,一共 5 個 5。
因爲setTimeout會在和他同時執行的代碼執行完成之後,再按照時間執行花括號裏面的內容。也就是說,除了console.log(i)這句話,其他都是同時執行的,1000*i也會根據5次循環依次賦值。循環完成之後,再同時開始執行5個setTimeout,這時i已經爲5。所以雖說時間是0秒~4秒,但因爲同一起跑線開始,所以是每隔一秒輸出一個5。

那應該怎麼改才能輸出 0 到 4 呢?

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

添加了一個閉包來記錄i。

那如果刪掉i呢?

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

結果又會變成5,因爲內部其實沒有對 i 保持引用。

接着變:

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

結果:立馬!輸出 0 到 4(注意是立馬!)
這裏給 setTimeout 傳遞了一個立即執行函數,自調用函數執行完後返回undefine。也就等價於:

setTimeout(undefined, ...);

也就是在立馬輸出0~4後,延時器開始按時間執行,0秒……4秒,但是什麼都沒幹,因爲執行內容爲undefined。
這裏就算把i去掉,一個空傳參的自執行函數,也可以達到同樣的效果

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

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

以上兩種是一樣的,都會一下同時輸出0~4,因爲:
自調用在聲明的時候調用,不是計時器調用的,而聲明是在走循環的時候,也就是循環的時候就執行了。所以傳不傳i唯一的區別就是:用的是局部變量i還是全局變量i,結果沒區別

最後一個!

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for( var i=0 ; i<10000 ; i++ ) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);

結果:2 3 5 4 1
首先先碰到一個 setTimeout,於是會先設置一個定時,在定時結束後將傳遞這個函數放到任務隊列裏面,因此開始肯定不會輸出 1 。

然後是一個 Promise,裏面的函數是直接執行的,因此應該直接輸出 2 3 。

然後,Promise 的 then 應當會放到當前 tick 的最後,但是還是在當前 tick 中。

因此,應當先輸出 5,然後再輸出 4 。

最後在到下一個 tick,就是 1 。
(其實最後一題我不是很懂,只是大概查了一下。歡迎批評指正!)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章