再談Js閉包

不知不覺距離上一篇閉包文章已通過了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就已經能夠解決全部問題了,因此在實際開發中應該避免第一種構建閉包的解決方案。

相關文章
相關標籤/搜索