執行上下文(棧)/作用域(鏈)/with

執行上下文(棧)context(context stack)

每一次代碼執行和函數調用都會產生一個執行環境,稱爲執行上下文。

一個執行上下文(caller)又可以激活(調用)另一個執行上下文(callee),這時caller會暫停自身的執行把控制權交給callee進入callee的執行上下文,callee執行完畢後將控制權交回caller,callee可以用return或者拋出Exception來結束自己的執行。

多個執行上下文會形成執行上下文棧,最頂層是當前執行上下文,底層是全局執行上下文。

Figure 4. An execution context stack.

作用域(鏈)scope(scope chain)

作用域是每一個執行上下文自身持有的活動對象的集合,如在本執行上下文中聲明的變量和函數以及方法參數傳入的對象。

每一個執行上下文可以訪問的對象包括自身的作用域和父執行上下文的作用域和父父執行上下文作用域直到全局作用域,這就產生了作用域鏈

作用域鏈的工作原理跟原型鏈十分相似:如果本身的作用域中查找不到標識符,那麼就查找父作用域,直到頂層。

目前假設作用域的聯動是用的__parent__對象,它指向作用域鏈的下一個對象。(在ES5中,確實有一個outer鏈接)

Figure 9. A scope chain.

全局上下文的作用域包含Object.prototype中的對象,with和catch會改變作用域鏈:

Object.prototype.x = 10; 
var w = 20;
var y = 30; 
// in SpiderMonkey global object
// i.e. variable object of the global
// context inherits from "Object.prototype",
// so we may refer "not defined global
// variable x", which is found in
// the prototype chain 
console.log(x); // 10 
(function foo() { 
  // "foo" local variables
  var w = 40;
  var x = 100; 
  // "x" is found in the
  // "Object.prototype", because
  // {z: 50} inherits from it 
  with ({z: 50}) {
    console.log(w, x, y , z); // 40, 10, 30, 50
  } 
  // after "with" object is removed
  // from the scope chain, "x" is
  // again found in the AO of "foo" context;
  // variable "w" is also local
  console.log(x, w); // 100, 40 
  // and that's how we may refer
  // shadowed global "w" variable in
  // the browser host environment
  console.log(window.w); // 20 
})();

在with中,查詢__parent__之前會先去查詢__proto__,會使作用域鏈增大。

Figure 10. A "with-augmented" scope chain.

只要所有外部函數的變量對象都存在,那麼從內部函數引用外部數據則沒有特別之處——我們只要遍歷作用域鏈表,查找所需變量。

注意:通過構造函數創建的方法的作用域鏈中只有全局變量!

var x = 10; 
function foo() { 
  var y = 20; 
  function barFD() {
    alert(x);
    alert(y);
  } 
  var barFE = function () {
    alert(x);
    alert(y);
  }; 
  var barFn = Function('alert(x); alert(y);'); 
  barFD(); // 10, 20
  barFE(); // 10, 20
  barFn(); // 10, "y" is not defined 

foo();

二維作用域鏈查找
源於ECMAScript的原型特性。如果一個屬性在對象中沒有直接找到,查詢將在原型鏈中繼續。即常說的二維鏈查找。(1)作用域鏈環節;(2)每個作用域鏈--深入到原型鏈環節:
function foo() {
  alert(x);

Object.prototype.x = 10; 
foo(); // 10

然而,如上文所提及,當一個上下文終止之後,其狀態與自身將會被 銷燬(destroyed) ,同時內部函數將會從外部函數中返回。

此外,這個返回的函數之後可能會在其他的上下文中被激活,那麼如果一個之前被終止的含有一些自由變量的上下文又被激活將會怎樣?通常來說,解決這個問題的概念在ECMAScript中與作用域鏈直接相關,被稱爲 (詞法)閉包((lexical) closure)。

引出的閉包將在下章討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章