执行上下文(Execution Context):
当 JavaScript 代码执行的时候,会进入不同的执行上下文,这个上下文决定了变量或者函数可以访问哪些数据,以及他们的行为。多个不同的执行上下文就构成了一个执行上下文栈(Execution Context Stack)。
例如:
var a = "global var";
function foo() {
console.log(a);
}
function outerFunc() {
var b = "var b in outerFunc";
console.log(b);
function innerFunc() {
var c = "var c in innerFunc";
console.log(c);
foo();
}
return innerFunc();
}
outerFunc();
代码首先进入 Global Execution Context,然后依次进入 outerFunc,innerFunc,foo 的执行上下文, 执行上下文栈就可以表示为:
每个执行上下文都有三个重要的属性:变量对象、作用域链、this。
变量对象(Variable Object):
每个上下文都有一个关联的变量对象,上下文中定义的所有变量和函数都存在于这个对象上。
一般 VO 会包含以下信息:
变量(Variable Declaration)
函数声明(Function Declaration)
函数的形参
当 JavaScript 代码运行中,如果试图寻找一个变量,会先去 VO 中寻找。
对于前面的例子,Global Execution Context 的 VO 可以表示如下:
注意:
(1)函数表达式(与函数声明相对)不包含在 VO 中。
(2)没有使用 var 声明的变量不在 VO 中。
活动对象(Activation Object):
活动对象包含在变量对象中,活动对象最大的特点就是 ”活动“ 两个字,它表示活动对象就是作用域链上正在被执行和引用的变量对象。有利于区别普通的变量对象。
对于前面的例子,outerFunc开始执行的时候,就会创建一个 AO:
Execution Context 细节:
当一段 JavaScript 代码被执行时,JavaScript 解释器会创建它的执行上下文,分两个阶段:
1.创建阶段(当函数被调用,但是开始执行代码前):
创建作用域链
创建 VO/AO
设置 this 的值
2.代码执行阶段:
设置变量的值
设置函数引用
解释/执行代码
对于创建 VO/AO 阶段,JavaScript 解释器主要做了以下事情:
1.根据函数的参数,创建并初始化 arguments
2.扫描函数内部,查找函数声明
对于所有找到的函数声明,将函数名和函数引用存入VO/AO 中
如果 VO/AO 中已经有同名的函数,那么就进行覆盖
3.扫描函数内部,查找变量声明
对于所有找到的变量声明,将变量名存入VO/AO中,并初始化为 "undefined"
如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性