setTimeout 與 閉包。。。

先看下面一個比較坑的代碼閉包

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

首先一個for循環, 會執行五次, setTimeout被執行了五次函數

但裏面的timer這時候並無執行, 而是依次在1 2 3 4 5秒後執行spa

此時只創建了全局上下文;code

 

timer放在了事件隊列裏面執行; timer執行時, for循環已經完成blog

全局做用域中的i變量值變成了6, 此時建立timer的做用域和做用域鏈隊列

 

由於timer裏面並無定義i, 也沒有給i賦值, 因此timer 在本身的做用域是找不到i的,事件

只能沿着做用域往上找, 找到全局做用域的i, 作用域

timeri 獲取全局做用域i, 也就是6, 因此5次timer都是輸出6io

 

當加入閉包的時候, 狀況就不同了。console

 

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

 

第一次 for循環的時候, 同時又有自執行函數

也就是在for執行的同時, 執行了匿名函數, 建立了匿名函數的做用域, 此時創建了匿名函數的上下文環境;

進入匿名函數的做用域的時候, 包含了一個內部函數 function(){console.log(j)}

同時這個函數被全局變量 window.setTimeout引用,這就造成了閉包!

 

在匿名函數執行完後,匿名函數的執行上下文出棧

也就是在for循環第一次執行完後,

匿名函數的的活動變量 j , 因爲閉包的關係,並無被銷燬,

而是保存在第一個window.setTimeout定時器中, 此時i 是1; j也是1; 這個值會一直保存在第一個定時器屬性中;

直到第一個定時器被銷燬;

 

而後進入第二次循環, 同理 又進入匿名函數, 建立了第二個閉包, 閉包的活動變量j 被第二個定時器引用;

也會保存在在第二個定時器中, 此時 i,j都是2;

 

依次類推, 3 4 5; 完成需求!

相關文章
相關標籤/搜索