執行環境、變量對象、做用域鏈、閉包

關於函數相關的一些概念,如執行環境、變量對象、做用域鏈、閉包,以前是詳細查閱了資料研究了一番的,可現在時間久了,不少東西有生疏了。javascript

記錄下本身的大腦思考過程,是個很好的學習過程。前端

1.執行環境。(execution context)java

由可執行的代碼建立。分爲3種。web

1)全局執行環境。當程序開始執行時即進入全局執行環境。全局執行環境進棧。瀏覽器

2)函數執行環境(局部執行環境)。每當調用一個函數時,就進入一個局部執行環境,該函數執行環境進棧。在執行return以後,該函數執行環境出棧,把控制權返回給以前的執行環境。閉包

3)eval()函數執行環境。使用js原生eval()函數時也會進入一個執行環境,執行環境進棧,在函數執行完畢後,執行環境出棧。ecmascript

 

全局執行環境是最外層的一個執行環境。在web瀏覽器中,全局執行環境被認爲是window對象,由於全部的全局變量和全局函數都是做爲window對象 的屬性和方法建立的。某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬(全局執行環境直到應用程序退出—— 例如關閉網頁或者瀏覽器——時纔會被銷燬),被垃圾回收機制回收。ide

 

2.變量對象(variable object)函數

每一個執行環境都有一個與之對應的變量對象。做爲執行環境的屬性,規定了執行環境中能夠操做的變量和函數。雖然咱們的代碼沒法訪問這個對象,可是解析器在處理數據時會在後臺使用它。分爲兩種。
       a. 全局執行環境的變量對象是全局變量,也就是全局對象自身--在程序開始時已經建立。這裏說的便是window/global對象
       b. 函數執行環境的變量對象成爲活動對象AO,---在進入函數執行環境時建立學習

 咱們剛纔說了變量對象決定了該做用域內可進行操做的函數和變量,那咱們具體說下VO包含了哪些東西。

變量對象由3部分組成:  1,該執行環境內的全部函數聲明。2.做爲形參傳遞進來的arguments 3.該執行環境內的全部變量聲明

 

3.做用域與做用域鏈

做用域便是函數和變量可被有效使用的範圍。

做用域鏈是當代碼在一個環境中執行時建立的,做用域鏈的用途就是要保證執行環境中能有效有序的訪問全部變量和函數。做用域鏈的最前端始終都是當前執行的代碼所在環境的變量對象,下一個變量對象是來自其父親環境,再下一個變量對象是其父親的父親環境,直到全局執行環境。

標識符解析是沿着做用域鏈一級一級地搜索標識符的過程。搜索過程始終從做用域鏈的前端開始,而後逐級地向後回溯,直到找到標識符爲止(若是找不到標 識符,一般會致使錯誤發生)。其實,通俗的理解就是:在本做用域內找不到變量或者函數,則在其父親的做用域內尋找,再找不到則到父親的父親做用域內尋找, 直到在全局的做用域內尋找!

重點看下做用域鏈是什麼:

  scope chain:

   一個函數執行環境的做用域鏈是在函數調用時開始建立的
   由這個函數執行環境的變量對象AO+scope property組成


  函數的scope property屬性:
    一個函數的scope property屬性是在這個函數建立(定義或叫聲明)的時候就存在了
    無論它是否有被調用(裏面放的是父級的變量對象)

 因此,一個執行環境的做用域鏈包括的是本身的AO和全部父級環境的AO。理解了這些就清楚了爲什麼內部函數能夠使用外部函數中定義的變量和函數。

 

 

4.執行環境、變量對象、做用於鏈的關係圖。

執行環境爲一個對象,VO(AO),this、做用鏈做爲對象的三個屬性看待。因此在進入一個執行環境時,跟它相關的這三個屬性也隨之肯定了。

 

activeExecutionContext = {
    VO: {...}, // or AO
    this: thisValue,
    Scope: [ // Scope chain
      // list of all variable objects
      // for identifiers lookup
    ]
};

 

5.垃圾回收機制。

 

 

6.談談閉包。

6-1)什麼是閉包。

說法一:閉包就是一個函數能夠訪問到另外一個函數的變量---感受等於沒說。

 
說法二: mdn認爲閉包是一個特殊的對象,包含了兩個部分(閉包函數和函數建立時的環境---即變量)---感受這個說法更貼近實際一些。

 

閉包,整體上是說依賴於做用域鏈和垃圾回收機制。要理解閉包最好從編譯器的角度來理解。

6-2)閉包的應用場景

 a.實現封裝。

 function Person(){
        var name = "default";

        return {
            getName : function(){
                return name;
            },
            setName : function(newName){
                name = newName;
            }
        }
    }

    var john = Person();
    console.log(john.getName());
    john.setName("john");
    console.log(john.getName());

    var jack = Person();
    console.log(jack.getName());
    jack.setName("jack");
    console.log(jack.getName());

 

 

6-3)閉包的特色

 

 

 7)建立函數的方式(定義函數的方式)

 因爲這裏主要講解的是函數相關的重要概念,因此補充下能夠經過哪些方式建立函數。

3種方法,

  Function構造器
  函數聲明
  函數表達式

 

 

8.this指向。

1)做爲普通函數調用。

var name = "The Window";
var obj={
    name: "My Object",
    getNameFunc: function () {
        return function(){
            return this.name;
        }
    }
};
console.log(obj.getNameFunc()());

 obj.getNameFunc()等同於函數function(){ return this.name; }。

因此obj.getNameFunc()再也不與obj有任何關係,因此它的調用僅僅是普通函數調用。在js的normal模式下,this指向window。在strict模式下,this爲undefined。

 

參考:

這裏的表達僅爲我的理解,比較官方的更精確的表述請見參考。

《javascript高級程序設計第3版》

 ECMAscript官方文檔。

http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

相關文章
相關標籤/搜索