JavaScript閉包,你理解嗎?

jopen 12年前發布 | 24K 次閱讀 JavaScript開發 JavaScript

簡言之,理解很多問題的關鍵是閉包只有在調用的時候才進行解析。


維基百科上對閉包的定義是: Closure (also lexical closure or function closure) is a function together with a referencing environment for the non-local variablesof that function.用我拙略的語言翻譯過來就是閉包(又稱“詞法閉包”或“函數閉包”)是一個包含了非本地變量引用環境的函數。

閉包其實就是一個函數;如果一個函數訪問了它的外部變量,那么它就是一個閉包。一個典型的例子就是全局變量的使用。所以從技術上來講,在Javascript中,每個function都是閉包,因為它總是能訪問在它外部定義的變量。


示例:

首先來看一個簡單的例子:

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
var sayAlert = say667();
sayAlert()

執行結果應該彈出667而不是666,這個應該很好理解。再來看一個容易迷惑的經典例子:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push( function() {alert(item + ' ' + list[i])} );
    }
    return result;
}
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList的執行結果是彈出item3 undefined窗口三次。因為這三個閉包是在同一個外部函數中定義的,item的值為最后計算的結果,但是當i跳出循環時i值為3,所以list[3]的結果為undefined.


將引用變為拷貝?

理解問題的關鍵是,Javascript是一門解釋性的語言,一個函數內部定義的另一個函數(即閉包)只有在調用的時候才進行解析buildList函數中定義閉包時,使用了參數"list"以及內部變量"i"引用,而不是拷貝。因此只有當閉包執行時,也就是在testList函數中調用時,才會開始引用list和i的值并輸出;而此時i的值為4,結果可想而知了!

為了達到預期的效果,我們來改造一下buildList函數,而改造的關鍵是在每次循環中創建變量i的拷貝,也就是將引用變為拷貝?一種簡單的方法就是使用自執行的“匿名函數”來對閉包進行包裹:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        (function(r){
            var item = 'item' + list[r];
            result.push( function() {alert(item + ' ' + list[r])} );
        })(i);
    }
    return result;
}

這樣,在函數buildList執行的時候,匿名函數會立即執行,并把i作為參數;此時匿名函數內部的變量r相當于有了i的一個拷貝,而r的值是不會被外部的循環改變的。因此函數testList的執行結果是分別彈出“item1 1”、“item2 2”、“item3 3”。?


你理解了嗎??

要小心的是,在Javascript函數參數傳遞的時候,只有基本類型的參數會被拷貝,對象類型的參數傳遞的是引用。因此,如果給匿名函數傳遞對象類型的參數時(沒有人會這么做吧!),要小心出現意外的情況;舉個變態的例子:

function buildList(list) {
    var result = [];
    var obj = {};
    for (obj.i = 0; obj.i < list.length; obj.i++) {
        (function(r){
            var item = 'item' + list[r.i];
            result.push( function() {alert(item + ' ' + list[r.i])} );
        })(obj);
    } 
    return result;
}

函數testList的執行結果是什么呢?是分別彈出“item1 undefined”、“item2 undefined”、“item3 undefined”??窗口,跟前面兩種寫法的結果都不一樣。原因是匿名函數立即執行后,其內部變量item被正確賦值,等到testList函數運行時,閉包中引用的r.i其實就是obj對象的i變量,它的值當然是3,結果就可想而知了。

閉包,你理解了嗎??

轉自:http://blog.csdn.net/neareast/article/details/7628455

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!