原文地址数组
有不少文章已经对ECMAScript的核心概念作了详尽解读。本系列文章翻译自Dmitry Soshnikov的我的网站,相信很多人已经看过原文或者译文。原文简洁易懂而且严谨,条理清晰地阐明了全部JavaScript开发者不得不深刻理解的ECMAScript核心概念。重复翻译的缘由主要是为了我的收藏、整理之用。初次翻译,技巧拙劣,若有不足,请不吝赐教。安全
在这篇文章中咱们将提到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代码更加复杂。在这种状况下,有一个概念叫调用上下文
(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