本文基于ECMA-262-3标准深入解析JavaScript执行上下文(EC)机制。详细阐述全局代码、函数代码及Eval代码在调用堆栈中的入栈与出栈过程,结合SpiderMonkey引擎对调用上下文传递的扩展实现,为理解变量对象与作用域链奠定理论基础。
本文通过具体实例解析JavaScript中的ECMA执行上下文概念,旨在为读者提供清晰的参考与理解。
介绍
本文将重点探讨ECMAScript的执行上下文及其关联的可执行代码行为。
定义
每当程序控制权转移至ECMAScript可执行代码时,系统即会进入一个执行上下文。
执行上下文(简称EC)是一个抽象概念,ECMA-262标准利用该概念来区分可执行代码(executable code)。
标准规范并未从技术实现层面精确定义EC的类型与结构,这属于具体ECMAScript引擎实现时需考虑的问题。
活动的执行上下文在逻辑上构成一个堆栈。堆栈底部始终固定为全局上下文(global context),而堆栈顶部则指向当前活动的执行上下文。随着各类EC类型的入栈或出栈,该堆栈结构会动态修改。
可执行代码
可执行代码的概念与抽象的执行上下文概念相对立。在某些特定时刻,可执行代码与执行上下文的概念是等价的。
例如,我们可以定义一个数组来模拟执行上下文堆栈的行为:
ECStack = [];
每次进入函数(无论该函数是被递归调用还是作为构造函数使用),或者内置的eval函数执行时,这个堆栈都会被推入新元素。
全局代码
这类代码在“程序”层级进行处理,例如加载外部JS文件或执行本地<script></script>标签内的代码。需要注意的是,全局代码不包含任何函数体内的代码。
在初始化(程序启动)阶段,ECStack的状态如下:
ECStack = [
globalContext
];
函数代码
当进入函数代码(涵盖所有类型的函数)时,ECStack会被推入新元素。需注意,具体的函数代码并不包括内部函数(inner functions)的代码。如下示例展示了函数通过自调用方式递归执行一次的情况:
(function foo(bar) {
if (bar) {
return;
}
foo(true);
})();
此时,ECStack的变化过程如下:
// first activation of foo
ECStack = [
<foo> functionContext
globalContext
];
// recursive activation of foo
ECStack = [
<foo> functionContext – recursively
<foo> functionContext
globalContext
];
每次返回当前存在的执行上下文并弹出ECStack中相应的执行上下文时,栈指针会自动移动位置,这是一种典型的堆栈实现方式。若存在未被捕获的异常,同样会涉及一个或多个执行上下文。当相关代码段执行完毕直至应用程序结束,ECStack中仅保留全局上下文(global context)。
Eval 代码
eval代码具有其特殊性,它涉及一个概念:调用上下文(calling context),即在调用eval函数时产生的上下文。eval中的变量或函数声明活动会影响调用上下文(calling context)。
eval('var x = 10');
(function foo() {
eval('var y = 20');
})();
alert(x); // 10
alert(y); // "y" is not defined
ECStack的变化过程如下:
ECStack = [
globalContext
];
// eval('var x = 10');
ECStack.push(
evalContext,
callingContext: globalContext
);
// eval exited context
ECStack.pop();
// foo funciton call
ECStack.push(<foo> functionContext);
// eval('var y = 20');
ECStack.push(
evalContext,
callingContext: <foo> functionContext
);
// return from eval
ECStack.pop();
// return from foo
ECStack.pop();
由此可见,这是一个非常普通的逻辑调用堆栈。
在版本号1.7以上的SpiderMonkey(内置于Firefox、Thunderbird)实现中,可以将调用上下文作为第二个参数传递给eval。如果该上下文存在,则有可能影响“私有”(类似于使用private关键字命名的变量)变量。
function foo() {
var x = 1;
return function () { alert(x); };
};
var bar = foo();
bar(); // 1
eval('x = 2', bar); // pass context, influence on internal var "x"
bar(); // 2
结论
本文内容是为未来分析其他与执行上下文相关的主题(例如变量对象、作用域链等)所必需的理论基础,这些主题将在后续章节中详细展开。
其他参考
本文内容对应ECMA-262-3标准规范中的相关章节。
英文地址 : ECMA-262-3 in detail. Chapter 1. Execution Contexts
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。