萬盛學電腦網

 萬盛學電腦網 >> 腳本專題 >> javascript >> JavaScript 作用域鏈解析

JavaScript 作用域鏈解析

   JavaScript中有Scope(作用域),Scope chain(作用域鏈),Execute context(執行上下文),Active Object (活動對象),Dynamic Scope(動態作用域),Closure(閉包)這些概念,要理解這些概念,我們從靜態和動態兩個方面去分析一下。

  首先我們寫一個簡單的function來做一個例子:

  代碼如下:

  function add(num1, num2){

  var sum = num1 + num2;

  return sum;

  }

  我們定義了一個具有兩個形參的add函數。

  靜態方面:

  當創建add函數的時候,Javascript引擎會創建add函數的Scope chain,這個作用域鏈指向了Global Context(全局上下文)。如果用圖形形象化的表述如下圖所示:

JavaScript 作用域鏈解析 三聯

  從上圖可以看出,當add函數創建的時候,作用域鏈就已經創建了,因此可以得出一個結論,函數的作用域鏈是創建函數的時候就已經創建了,而不是動態運行期。下面就來看看動態運行期的時候會發生什麼事情。

  動態方面:

  當執行add函數的時候,JavaScript會創建一個Execute context(執行上下文),執行上下文中就包含了add函數運行期所需要的所有信息。Execute context也有自己的Scope chain,當函數運行的時候,JavaScript引擎會首先從用add函數的作用域鏈來初始化執行上下文的作用域鏈,然後JavaScript引擎又會創建一個Active Object,這個對象裡面包含了函數運行期的所有局部變量,參數以及this等變量。

  如果形象的描述add函數動態運行期會發生什麼,可以用如下圖來描述:

  從上圖可以看出,執行上下文是一個動態的概念,它是當函數運行的時候創建的,同時Active Object對象也是一個動態的概念,它是被執行上下文的作用域鏈引用的。因此可以得出一個結論:執行上下文和活動對象都是動態概念,並且執行上下文的作用域鏈是由函數作用域鏈初始化的。

  上面說了函數作用域和執行上下文作用域,下面接著說一下動態作用域的問題,當在JavaScript通過with語句,try-catch的catch子句,以及eval方法的時候,JavaScript引擎就會動態的改變執行上下文的作用域。下面還是通過一個例子來看看:

  代碼如下:

  function initUI(){

  with (document){ //avoid!

  var bd = body,

  links = getElementsByTagName("a"),

  i= 0,

  len = links.length;

  while(i < len){

  update(links[i++]);

  }

  getElementById("go-btn").onclick = function(){

  start();

  };

  bd.className = "active";

  }

  當執行上面的initUI函數的時候,JavaScript會動態的創建一個with語句對應的作用域放到執行上下文作用域鏈的最前端,通過下圖可以形象的描述上述過程,下圖紅色標注的區域就顯示了with語句產生的作用域。

  最後,我們來看看JavaScript最神秘的Closure(閉包),閉包在JavaScript其實就是一個函數,閉包是在函數運行期被創建的,下面還是以一個實例來看看:

  代碼如下:

  function assignEvents(){

  var id = "xdi9592";

  document.getElementById("save-btn").onclick = function(event){

  saveDocument(id);

  };

  }

  當上面的assignEvents函數被執行的時候,會創建一個閉包,而這個閉包會引用assignEvents作用域中的id變量,如果按照傳統的編程語言的方式,id是存儲在堆棧上的一個變量,當函數執行完了以後id就消失,那麼怎麼可能再次引用呢?顯然這裡JavaScript采用了另外的方式。下面就來看看JavaScript是如何來實現閉包的。當執行assignEvents函數的時候,JavaScript引擎會創建assignEvents函數執行上下文的作用域鏈,這個作用域鏈包含了assignEvents執行時的活動對象,而同時JavaScript引擎也會創建一個閉包,而閉包的作用域鏈也會引用assignEvent執行時候的活動對象,這樣當assignEvents執行完的時候,雖然它本身執行上下文的作用域鏈不再引用活動對象了,但是閉包還是引用著assignEvents運行期對應的活動對象,這就解釋了JavaScipt內部的閉包機制。可以用下圖形象的表述上面assignEvents函數運行期的情形:

  從上面可以看出,當assignEvents函數執行完畢以後,document.getElementById("save-btn").onclick引用了閉包,這樣當用戶點擊save-btn的時候,就會觸發閉包的執行,那麼下面就來看看閉包執行時的情形。前面也說了JavaScript中閉包其實就是函數,因此閉包執行和函數執行時的情形是一致的,通過下圖來形象的描述上述onclick事件所關聯的閉包。

  從上圖可以看出JavaScript引擎首先創建了閉包的執行上下文,然後用閉包作用域鏈來初始化閉包的執行上下文作用域鏈,最後再將閉包執行時對應的活動對象放入到作用域的最前端,這也進一步驗證了閉包就是函數的論斷。

copyright © 萬盛學電腦網 all rights reserved