先看下面一個比較坑的代碼閉包
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; 完成需求!