for循環+setTimeout的延遲操做

例子:

1 for (var i = 0; i < 5; i++) {
2     setTimeout(function () {
3         console.log(i);
4     }, 100)
5 }

上述代碼,輸出結果顯而易見是5個5,且並無任何的延遲效果。那麼爲何呢?閉包

       首先這樣的結果須要從JS的執行機制提及。JS是單線程環境,也就是說代碼的執行是從上到下,依次執行。這樣的執行稱爲同步執行。由於種種不要浪費和節約的緣由。JS中引進了異步的機制。在這段代碼中,哪一個是同步哪一個是異步呢?for循環是同步代碼,而setTimeout中的是異步代碼。那麼JS碰到這個有同步和異步的狀況下會先從上到下執行同步代碼,碰到異步的代碼會將其插入到任務隊列當中等待。而setTimeout是延時,也就是說碰到setTimeout這個異步的代碼塊會根據它裏面的第二個參數:延時時間來將代碼插入到任務隊列當中,好比上面這段代碼中,第二個參數延時時間是100,也就是說執行到它的時候會在100ms以後將它插入到任務隊列當中。同步代碼都執行完成以後,那麼JS引擎就空閒了,這個時候就輪到任務隊列中的異步代碼依次加載了。
      這是上面這段代碼的答案的一半。另外一半就來自於做用域,做用域是變量等資源的做用範圍。在這段代碼中準確的說是做用域鏈的問題,當同步代碼執行完畢開始執行異步的setTimeout代碼時,setTimeout中須要一個變量i,而執行的時候在當前的做用域中開始找,找不到變量i的定義,這個時候就把建立這個函數的做用域做爲當前做用域,再次尋找,建立這個函數的做用域就是全局做用域,也就是找到了for循環中i,找到了以後就結束尋找變量i的行程。因爲這個時候的i是全局的,並且人家已經變爲了最終形態:5,setTimeout找到的就是這個i=5;因此就輸出了5,下面的4次setTimeout 的執行都是相似,因此結果都是5;
因此我對這個答案的理解歸結起來就是  異步加載+做用域鏈。異步

正確寫法:

1 for (var i = 0; i < 5; i++) {
2     (function (i) {
3         setTimeout(function () {
4             console.log(i);
5         }, 100 * i);
6     })(i);
7 }

將延遲操做閉包,當即執行。函數

相關文章
相關標籤/搜索