ECMA-262-3詳解(1)執行上下文

原文地址數組

做者的話

有不少文章已經對ECMAScript的核心概念作了詳盡解讀。本系列文章翻譯自Dmitry Soshnikov的我的網站,相信很多人已經看過原文或者譯文。原文簡潔易懂而且嚴謹,條理清晰地闡明瞭全部JavaScript開發者不得不深刻理解的ECMAScript核心概念。重複翻譯的緣由主要是爲了我的收藏、整理之用。初次翻譯,技巧拙劣,若有不足,請不吝賜教。安全

正文

  1. 介紹
  2. 定義
  3. 可執行代碼的類型ecmascript

    1. 全局代碼
    2. 函數代碼
    3. Eval代碼
  4. 結論

介紹

在這篇文章中咱們將提到ECMAScript的執行上下文和與之相關的可執行代碼類型。ide

定義

每次當控制轉移到ECMAScript可執行的代碼,控制就進入了一個執行上下文。函數

執行上下文(Execution context,縮寫:EC)是ECMA-262標準使用的抽象概念,用來分類和區別一段可執行代碼

標準沒有從技術實現角度定義EC的準確結構和類型,這是ECMAScript引擎如何實現標準的問題。
邏輯上講,活躍的執行上下文集合組成了一個棧。棧底是全局上下文(global context),棧頂是當前(活躍)執行上下文。在進入和退出不一樣的EC時,棧被修改(pop/push)。網站

可執行代碼的類型

可執行代碼的類型的概念與執行上下文的抽象概念相關。講到代碼類型,在特定時候,它能夠指執行上下文。firefox

舉個例子,咱們將執行上下文定義成一個數組。翻譯

ECStack = [];

每次進入一個函數,棧會被壓入一個上下文(即便是函數被遞歸調用,或者做爲構造函數被調用),在eval函數中也是如此。code

全局代碼

這個類型的代碼在程序Program)層面被處理:好比加載的外部js文件、內聯代碼(在<script></script>標籤內)。全局代碼不包含任何函數體內的代碼。對象

在初始化(程序開始)時,ECStack看起來像這樣:

ECStack = [
  globalContext
];

函數代碼

在進入函數代碼(全部類型的函數)時,ECStack會被塞入新的元素。須要注意到:所說的函數代碼不包含內部函數的代碼。

舉個例子,咱們看看這個遞歸調用自身一次的函數:

(function foo(flag) {
  if (flag) {
    return;
  }
  foo(true);
})(false);

而後,ECStack被修改爲下面這樣:

// first activation of foo
ECStack = [
  <foo> functionContext
  globalContext
];
  
// recursive activation of foo
ECStack = [
  <foo> functionContext – recursively 
  <foo> functionContext
  globalContext
];

每次函數返回會退出當前執行上下文,ECStack彈出一個元素。當這段代碼的工做結束,ECStack再一次地僅包含globalContext-直到程序退出。

一個拋出可是沒有捕獲的異常可能致使退出一個或多個執行上下文:

(function foo() {
  (function bar() {
    throw 'Exit from bar and foo contexts';
  })();
})();

Eval代碼

eval代碼更加複雜。在這種狀況下,有一個概念叫調用上下文calling context),好比eval函數被調用的地方的上下文:

// influence global context
eval('var x = 10');
 
(function foo() {
  // and here, variable "y" is
  // created in the local context
  // of "foo" function
  eval('var y = 20');
})();
  
alert(x); // 10
alert(y); // "y" is not defined

注意,在ES5的嚴格模式中,eval已經不會影響 調用上下文,而是在本地沙箱中運行代碼。

對於上面的例子,咱們有以下的ECStack修改:

ECStack = [
  globalContext
];
  
// eval('var x = 10');
ECStack.push({
  context: evalContext,
  callingContext: globalContext
});
 
// eval exited context
ECStack.pop();
 
// foo funciton call
ECStack.push(<foo> functionContext);
 
// eval('var y = 20');
ECStack.push({
  context: evalContext,
  callingContext: <foo> functionContext
});
 
// return from eval 
ECStack.pop();
 
// return from foo
ECStack.pop();

很是隨意而正常的調用棧。
在老版的SpiderMonkey 實現(firefox)中,最多到1.7版本,能夠將 調用上下文做爲第二個參數傳給eval函數。所以,若是上下文任存在,會影響到私有變量:

function foo() {
  var x = 1;
  return function () { alert(x); };
};
 
var bar = foo();
 
bar(); // 1
 
eval('x = 2', bar); // pass context, influence internal var "x"
 
bar(); // 2

然而,因爲現代引擎的安全問題,它被修復而不在有意義。

ES2015+引入了一種新的代碼類型-模塊代碼

結論

這些基礎的理論須要被用來更深刻地研究與執行上下文相關的細節,好比變量對象做用域鏈,這些描述能夠在適當的章節被找到。

原始做者:Dmitry Soshnikov
原始發佈時間:2009-06-26

相關文章
相關標籤/搜索