JavaScript的执行上下文
JavaScript的执行上下文一、执行上下文的理解
每个执行上下文都与一个作用域链(scope chain)关联起来。该作用域链用来在function执行时求出标识符(identifier)的值。该链中包含多个对象,在对标识符进行求值的过程中,会从链首的对象开始,然后依次查找后面的对象,直到在某个对象中找到与标识符名称相同的属性。在每个对象中进行属性查找时,会使用该对象的prototype链。在一个执行上下文中,与其关联的作用域链只会被with语句和catch 子句影响。
活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在执行上下文类型进入和退出上下文的时候被修改(推入或弹出)。
二、在进入一个新的执行上下文时,会按顺序执行下面的操作
1、创建激活(activation)对象
激活对象是在进入新的执行上下文时创建出来的,并且与新的执行上下文关联起来。在初始化构造函数时,该对象包含一个名为arguments的属性。激活对象在变量初始化时也会被用到。JavaScript代码不能直接访问该对象,但可以访问该对象的成员(如 arguments)。
2、创建作用域链
接下来的操作是创建作用域链。每个 function 都有一个内部属性[[scope]],它的值是一个包含多个对象的链。该属性的具体值与 function 的创建方式和在代码中的位置有很大关系(见本建议后面介绍的“function 对象的创建方式”内容)。此时的主要操作是将上一步创建的激活对象添加到 function 的[[scope]]属性对应的链的前面。
3、变量初始化
这一步对function中需要使用的变量进行初始化。初始化时使用的对象是创建激活对象过程中所创建的激活对象,不过此时称做变量对象。会被初始化的变量包括 function 调用时的实际参数、内部function和局部变量。在这一步中,对于局部变量,只是在变量对象中创建了同名的属性,其属性值为undefined,只有在 function 执行过程中才会被真正赋值。全局JavaScript代码是在全局执行上下文中运行的,该上下文的作用域链只包含一个全局对象。
三、实例
1、例如,我们可以定义执行上下文堆栈是一个数组:
ECStack = [];
2、全局代码
这种类型的代码是在"程序"级处理的:例如加载外部的js文件或者本地<script></script>标签内的代码。全局代码不包括任何function体内的代码。
在初始化(程序启动)阶段,ECStack是这样的:
ECStack = [
globalContext
];
3、函数代码
当进入funtion函数代码(所有类型的funtions)的时候,ECStack被压入新元素。需要注意的是,具体的函数代码不包括内部函数(inner functions)代码。如下所示,我们使函数自己调自己的方式递归一次:
(function foo(bar){
if (bar)
{
return;
}
foo(true);
})();
那么,ECStack以如下方式被改变:
// 第一次foo的激活调用
ECStack = [
<foo> functionContext
globalContext
];
// foo的递归激活调用
ECStack = [
<foo> functionContext – recursively
<foo> functionContext
globalContext
];
每次return的时候,都会退出当前执行上下文的,相应地ECStack就会弹出,栈指针会自动移动位置,这是一个典型的堆栈实现方式。一个抛出的异常如果没被截获的话也有可能从一个或多个执行上下文退出。相关代码执行完以后,ECStack只会包含全局上下文(global context),一直到整个应用程序结束。
3、Eval 代码
eval函数调用的时候产生的上下文。eval(变量或函数声明)活动会影响调用上下文(calling context)。
eval('var x = 10');
(function foo() {
eval('var y = 20');
})();
alert(x); // 10
alert(y); // "y" 提示没有声明
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();