ECMA-262-3详解(1)实行上下文

原文地点

作者的话

有许多文章已对ECMAScript的中心观点做了详实解读。本系列文章翻译自Dmitry Soshnikov的个人网站,置信不少人已看过原文或许译文。原文简约易懂而且严谨,条理清晰地阐清楚明了一切JavaScript开发者不能不深切明白的ECMAScript中心观点。反复翻译的缘由重要是为了个人珍藏、整顿之用。首次翻译,技能低劣,若有不足,请不吝赐教。

正文

  1. 引见
  2. 定义
  3. 可实行代码的范例

    1. 全局代码
    2. 函数代码
    3. Eval代码
  4. 结论

引见

在这篇文章中我们将提到ECMAScript的实行高低文和与之相干的可实行代码范例。

定义

每次当掌握转移到ECMAScript可实行的代码,掌握就进入了一个实行高低文。

实行高低文(Execution context,缩写:EC)是ECMA-262规范运用的抽象观点,用来分类和区分一段可实行代码

规范没有从手艺完成角度定义EC的正确组织和范例,这是ECMAScript引擎怎样完成规范的题目。
逻辑上讲,活泼的实行高低文集合组成了一个栈。栈底是全局高低文(global context),栈顶是当前(活泼)实行高低文。在进入和退出差别的EC时,栈被修正(pop/push)。

可实行代码的范例

可实行代码的范例的观点与实行高低文的抽象观点相干。讲到代码范例,在特定时刻,它能够指实行高低文。

举个例子,我们将实行高低文定义成一个数组。

ECStack = [];

每次进入一个函数,栈会被压入一个高低文(即使是函数被递归挪用,或许作为组织函数被挪用),在eval函数中也是云云。

全局代码

这个范例的代码在顺序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

    原文作者:freepoix
    原文地址: https://segmentfault.com/a/1190000015966386
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞