javascript程式碼執行機制簡單介紹

admin發表於2017-04-15

本文將通過一個簡單的程式碼例項介紹一下javascript引擎對程式碼從定義到執行的相關過程,從而加深對作用域作用域鏈等概念的理解。

在閱讀本文之前最好參閱以下幾篇文章:

(1).執行上下文可以參閱javascript執行上下文詳解一章節。

(2).變數物件可以參閱javascript變數物件詳解一章節。

(3).作用域和作用域鏈可以參閱javascript 作用域和作用域鏈詳解一章節。

以下面的程式碼為例子做一下介紹:

[JavaScript] 純文字檢視 複製程式碼
var x = 1;//定義全域性變數x,並賦值為1
function wrap(y){//定義一個全域性函式
  var x = 2;//定義一個區域性變數,並賦值為2
  function inner(z){//定義一個區域性函式
    console.log(x+y+z);//輸出值
  }
  return inner;//返回內部函式
}
var f = wrap(3);//將返回的內部函式賦值給變數f
f(1);//呼叫函式

上面的程式碼是一個典型的閉包的引用,關於閉包可以參閱javascript閉包概念介紹一章節。

第一階段:

首先會建立一個全域性物件,它的屬性或者方法可訪問性也是全域性的。

全域性物件具有唯一性,並且生命週期會伴隨整個應用程式。

全域性物件建立時,Math,String,Date和document等物件作為其屬性存在。

全域性物件通常只能夠使用別名來訪問,比如在web中就是用window來訪問全域性物件,可以用虛擬碼表示如下:

[JavaScript] 純文字檢視 複製程式碼
//建立一個全域性物件
var global = { 
  Math:{},
  String:{},
  Date:{},
  document:{},
  //其他屬性
  window:this
}

這個時候,javascript還會構建一個執行環境棧( Execution Context Stack) 。

與此同時,全域性執行環境上下文Execution Context(EC)也會被建立,並將其壓入執行環境棧中。

執行環境棧的建立目的是為了保證程式碼能夠按照正確的順序執行。

全域性執行環境上下文中還會生一個變數物件(Varibale Object) VO,並把VO指向全域性物件。

VO包含全域性物件中固有的一些屬性,比如Math,String,Date和document等,也包含自定義的函式或者變數。

函式在宣告的同時為其建立一個[[Scope]]內部屬性,表示如下:

[JavaScript] 純文字檢視 複製程式碼
wrap.[[Scope]] = [
  globalContext.VO
];

此時執行環境棧可以用虛擬碼表示如下:

[JavaScript] 純文字檢視 複製程式碼
ECStack = [  //執行環境棧
  EC(G) = {  //全域性執行環境上下文
    VO(G):{ //全域性變數物件
      ... //包含全域性物件原有的屬性
      x = 1; //定義變數x
      wrap = function(){}; //定義函式wrap
      A[[scope]] = [globalContext.VO];
    }
  }
];

第二階段:

當呼叫wrap()函式之後,會建立一個函式執行上下文,並將這個執行上下文壓入執行環境棧的頂部。

此時執行環境棧中有兩個執行環境上下文,分別是全域性執行環境上下文和函式wrap執行環境上下文。

wrap的執行環境上下文在棧頂,全域性執行環境上下文在棧的底部。

函式執行上下文中也會建立變數物件(Varibale Object) VO,不過函式的變數物件通常稱之為活動物件(Activation Object) A0。活動物件A0包含函式的形參、arguments物件、this物件、以及區域性變數和內部函式的定義。

當執行環境上下文被建立時 ,函式wrap的作用域鏈(Scope Chain)就會被建立 ,用於識別符號解析。

作用域鏈(Scope Chain)= 當前VO+A[[scope]]。

此時的ECStack結構:

[JavaScript] 純文字檢視 複製程式碼
ECStack = [  //執行環境棧
  EC(wrap) = {  //A的執行環境
    [scope]:VO(G), //VO是全域性變數物件
    AO(wrap) : { //建立函式wrap的活動物件
      y:3,
      x:2, //定義區域性變數x
      inner:function(){}, //定義函式B
      A[[scope]]:[globalContext.VO];
      arguments:[],//在函式中訪問的arguments就是AO中的arguments
      this:window
    },
    scopeChain:[
      wrapContext.AO,//wrap函式變數物件
      globalContext.VO//全域性變數物件
    ]
  },
  EC(G) = {  //全域性執行環境上下文
    VO(G):{ //全域性變數物件
      ... //包含全域性物件原有的屬性
      x = 1; //定義變數x
      wrap = function(){}; //定義函式wrap
      A[[scope]] = [globalContext.VO];
    }
  }
];

對於內部函式執行也是同樣的道理,這裡就不多介紹了。

相關文章