前言
在第一节中,我们介绍了可执行代码以及执行上下文的相关知识,对于每个执行上下文,都有三个重要属性:
- 变量对象(
Variable object
,VO
) - 作用域、作用域链(
Scope chain
) this
指向问题
这节重点介绍变量对象。
一、变量对象
变量对象是执行上下文中的数据作用域,也可以叫数据存储对象,存储了在上下文中定义的变量和函数声明。
不同的执行上下文中变量对象会有所不同,有时候我们也会听到活动对象
这个概念,那他们之间有没有什么关联?下面来说说全局上下文中的变量对象和函数上下文中的变量对象。
二、全局执行上下文
全局执行上下文中的变量对象,说白了就是全局对象。就这么简单,这个对象是作用域链的头部,意味着在局部作用域找不到的变量或方法最后都会在全局对象中进行查找。
三、函数执行上下文
在函数上下文中,我们一般使用活动对象(activation object,AO) 来表示变量对象。
从对象的作用来看,活动对象和变量对象其实是一个东西。变量对象是在规范上或者是在引擎实现上的,不可以在JavaScript环境中进行访问,当进入到一个函数上下文中,这个上下文中的变量对象才会被激活,而只有被激活的变量对象,它的属性才能够被访问到。
活动对象是在进入函数上下文时刻被创建,确定该函数在执行期间用到的诸如 this
、变量、arguments
对象以及函数等。
四、JavaScript运行过程
一段JavaScript代码会经过两个阶段进行处理:编译和执行。
编译阶段
当进入执行上下文时,此时会对当前运行环境的代码进行编译,此时变量对象会包括:
-
函数的所有形参 (如果是函数上下文)
- 由名称和对应值组成的一个变量对象的属性被创建
- 没有实参,属性值设为
undefined
-
函数声明
- 由名称和对应值(函数对象(
function-object
))组成一个变量对象的属性被创建 - 如果变量对象已经存在相同名称的属性,则完全替换这个属性
- 由名称和对应值(函数对象(
-
变量声明
- 由名称和对应值(
undefined
)组成一个变量对象的属性被创建; - 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
- 由名称和对应值(
例如:
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
c = 4;
console.log(c)
}
var a = 100;
在进入执行上下文后,这时候的 AO
是:
AO = {
arguments: {
0: 1,
length: 1
},
c: reference to function c(){},
a: 1,
b: undefined,
d: undefined
}
执行阶段
在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值
还是上面的例子,当代码执行完后,这时候的 AO
是:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
五、总结
- 每调用一个函数,
JavaScript
引擎会为其创建执行上下文,并把该执行上下文压入调用栈,然后JavaScript
引擎开始执行函数代码。 - 如果在一个函数
A
中调用了另外一个函数B
,那么JavaScript
引擎会为B
函数创建执行上下文,并将B
函数的执行上下文压入栈顶。 - 当遇到变量对象中存在相同的函数声明,则会直接覆盖;但是变量声明不会覆盖函数声明,函数声明的优先级较高。
- 当前函数执行完毕后,
JavaScript
引擎会将该函数的执行上下文弹出栈。