不知不覺距離上一篇閉包文章已通過了8個月了,如今的理解對比以前要健壯的多,再次總結下花生理解的閉包。javascript
閉包實際上就是子做用域讀取父做用域的變量,這原本很合理也很簡單,可是關鍵點在於這個讀取是動態的,請看下面的例子:java
for(var i=0 ;i<3 ;i++){ setTimeout(function(){ console.log(i); }); }; // 輸出 3 3 3
結果並非指望的0 1 2
,由於是動態的讀取i,所以若是你在下文改變變量i也依舊會影響到輸出讀取i。數組
傳統的解決方案是構建閉包,是最有效也是兼容性最好的方法瀏覽器
for(var i=0 ;i<3 ;i++){ (function(num){ setTimeout(function(){ console.log(num); }); })(i); }; // 輸出 0 1 2
這麼作是十分有效的,爲每個閉包單首創建一個做用域,也是下面要說的其餘解決方案的基礎。閉包
這一段代碼很重要,理解這一段代碼基本上就能夠說理解閉包了。函數
實際上,大多數狀況咱們並非想單純是使用for循環,for循環的一個很常見的用處是遍歷數組。code
var arr =['a' ,'b' ,'c']; for(var i=0 ;i<arr.length ;i++){ setTimeout(function(){ console.log(arr[i]); }); }; // 輸出 undefined undefined undefined
由於是動態讀取,因此輸出undefined很正常。可使用上面的構建一個自執行函數來解決,但還有一個更方便的解決方案,也是實際開發中常常用到的。ip
['a' ,'b' ,'c'].forEach(function(item){ setTimeout(function(){ console.log(item); }); }); // 輸出 a b c
利用Array原生的forEach能夠更好的實現,並且也符合語義,這個是花生最推薦的用法。作用域
若是瀏覽器較新支持ES5,Function還提供一個bind方法來綁定參數開發
for(var i=0 ;i<3 ;i++){ setTimeout(console.log.bind(null ,i)); }; // 輸出 0 1 2
Function.bind具體的語法與兼容性能夠參考MDN。
還有其餘的「歪門邪道」的解決方案,好比利用閉包讀取到父做用域的集合,在集合裏尋找「本身」,或者是利用js的引用傳遞等等。
實際上,利用ES5的bind方法和Array的forEach就已經能夠解決全部問題了,因此在實際開發中應該避免第一種構建閉包的解決方案。