天天看點

詳解JavaScript閉包

  要想完全明白JavaScript的閉包,要先明白js中的一些基礎原理,然後我再給出一些例子來講解閉包。

  在執行JavaScript時會建立一個執行環境(excution context),執行環境定義了變量或函數可以通路的其他資料。每個執行環境都有一個與之關聯的變量對象(variable object 有些地方叫域對象(Scope object)),在執行環境中定義的所有變量和函數都儲存在這個對象中。雖然我們編寫的代碼無法通路這個對象,但解析器在處理資料時會在背景使用它。

  全局執行環境是最外層的一個執行環境。根據js實作的宿主環境的不同,環境對象不一樣。浏覽器中,全局執行環境是window,node.js的全局變量是global,所有的全局變量和方法都儲存在全局對象中。

  每個函數都有自己的執行環境。當調用進入一個函數時,函數的執行環境就會被建立。代碼在執行環境中運作時,他建立用于儲存變量對象的作用域鍊(scope chain)。他的作用是儲存一個執行環境所有可以通路的變量或函數的有序集合。作用域的最前面是目前執行的代碼所在執行環境的變量對象。如果目前的執行環境是一個函數,就将函數的活動對象作為變量對象,剛開始時隻有一個變量arguments。作用域鍊中的下一個變量對象是包含目前環境變量的外部環境也就是他的調用者,再下一個是更外層的,至到全局執行環境。

  是以在一個執行中的方法内通路一個不存在于這個執行環境中的變量時不會報錯,解析器會從作用域鍊的頂端的變量對象開始找,如果找不到就找下一個執行環境的變量對象,一直到全局環境變量。如果有則停止查找。如果找到全局變量對象還是沒有發現,就會報錯。

  簡單說就是,一個函數體内就是一個執行環境,當一個函數在執行時,會建立一個作用鍊,這個鍊中有自己的變量對象,同時也有外層的變量對象。

  示例1:全局執行環境

var value1 =  1;
var value2 = 2;      

  直接運作上面的代碼,也就是說我們在一個全局執行環境中定義了兩個變量,是以他倆會被儲存在全局對象中這裡用global儲存。如下圖所示

詳解JavaScript閉包

示例2.全局環境中的方法

var value1 = 11;
var value2 = 22;

function log() {
    var logValue = "writing... ";
    console.log(logValue, "value1 :", value1, "  value2 ", value2);
}
log();      

  在log函數中,他的作用域鍊包含兩個對象:一個是自己的的變量對象(包含arguments對象)和全局環境變量對象,是以在函數内通路value1和value2時就可以沿着作用域鍊找找他倆。

詳解JavaScript閉包

 示例3:閉包(嵌套函數 )

var value1 = 11;
var value2 = 22;

function log() {
    var logValue = "writing... ";
    function nested() {
        console.log(logValue, "value1 :", value1, "  value2 ", value2);
    }
    return nested;
}
var fun1 = log();

fun1();      

  當你在一個函數内又建立了函數,那麼就會建立閉包。當函數開始執行時閉包就會在堆上配置設定堆棧幀,而且在函數傳回時不會被釋放掉。在上面的代碼中有三個執行環境一個是全局執行環境,一個是log()的局部執行環境,還有一個是nested()的執行環境。nested()可以通路log和global的變量.log()可以通路自己的global的變量:

詳解JavaScript閉包

  nested()函數從log()方法中被傳回,他的作用域鍊被初始化為log()中定義的所有活動對象,和全局變量對象,這樣nested()函數就可以通路所有的變量了。更重要的是log()執行完畢後,他的變量對象不會被銷毀,因為nested()函數仍然在引用這個變量對象。也可以說,log()函數執行完後,log()的作用域鍊被銷毀,但變量對象仍然保留在記憶體中,直到nested()銷毀後,引用的log()的變量對象才會被銷毀。

詳解JavaScript閉包

   有c++或c經驗的程式員,可能會認為傳回的是一個方法的指針,nested和fun1變量是兩個指向這個方法的指針,其實不然,c++言語指向方法的指針和JavaScript中對一個方法的引用有很大的不同,JavaScript中你可以認為一個方法的引用變量有一個指向方法的指針,同時也有一個隐藏的指針指向閉包。我就不再舉其他的例子了,能簡單明了的讓大家了解閉包的原理就夠了。

作者:李鵬

出處:http://www.cnblogs.com/li-peng/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀