JavaScript閉包理解的關鍵 - 做用域鏈

阮一峯的一篇文章已經對閉包的用途、概念講解地相對清晰了。javascript

閉包就是可以讀取其餘函數內部變量的函數。html

 

但我認爲裏面對於做用域鏈的解釋還不夠清晰,這裏做一些補充。java

閉包之因此能夠讀取外部函數的內部變量,即便外部函數已經返回,是由於它把外部函數的活動對象加到了本身的做用域鏈中。閉包

而理解做用域對於徹底理解閉包很重要。函數

 

做用域鏈是什麼?

對於這段代碼測試

    function compare(value1,value2){
        alert("this "+this+" arguments "+arguments);
        if(value1 < value2){
            return -1;
        }else if( value1 > value2){
            return 1;
        }else{
            return 0;
        }
    }

    var result = compare(5,10);

JavaScript高級程序設計裏這樣寫this

在建立函數compare()時,會建立一個預先包含全局變量的做用域鏈,當調用時,會爲函數建立一個執行環境。spa

其做用域鏈中包含兩個變量對象:本地活動對象和全局變量對象。設計

做用域鏈本質上是一個指向變量對象的指針列表。3d

畫出來大概是這個樣子的

 

舉個閉包的例子

    var name = "the window";

    var object = {
        name: "My Object",
        getNameFunc: function(){
            return function () {
                return this.name;
            }
        }
    }

不少人不明白爲何this.name不指向包含函數中的object.name。

對於這個問題,一樣是這本書中有一句話

每一個函數被調用時都會自動取得兩個特殊變量:this 和 arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止。

 

意思是,匿名函數的this是按做用域鏈搜索的,當在匿名函數自己的活動對象中就找到this後,就不會再向外搜索了。this指向的活動對象中並無name屬性,因此指向了全局的this。

 

做用域鏈畫出來是這樣的

 

如今明白了吧,閉包之因此有這樣的表現,是由於搜索標識符時按照做用域鏈,從內向外搜索。

再看下這個例子你會更清楚。

 

    var name = "the window";

    var object = {
        name: "My Object",
        getNameFunc: function(){
            var that = this;
            return function () {
                alert("that "+that);
                return that.name;
            }
        }

    }

 

變化在於這裏

 

匿名函數在自身的活動對象裏沒找到that對象,就沿做用域鏈向上找,在getNameFunc()的活動對象裏找到了that,指向的就是this。

因此你能夠按F12本身測試一下,輸出結果是"My Object"

相關文章
相關標籤/搜索