# 前言
执行上下文有如下 3 个重要属性:
- 变量对象
- 作用域链
- this
# 变量对象
变量对象时用于存储上下文中定义的变量和函数声明,全局上下文的变量对象就是全局对象(例如浏览器环境中的 window)
# 函数上下文中的变量对象
在函数上下文中我们用活动对象(activation object, AO) 来表示 变量对象。
活动对象和变量对象其实是同一个对象,只有当进入一个执行上下文中,这个变量对象才会被激活, 被激活的对象叫活动对象(AO),它通过函数的 arguments 属性初始化
# 变量对象(VO)和活动对象(AO)的区别
在未进入执行上下文之前,VO 中的属性不能访问,进入执行上下文之后 VO 变成 AO,AO 中的变量便可以访问, 同一个对象,处于执行上下文的不同声明周期
# 进入执行上下文
进入执行上下文,这时候代码还没执行,变量对象会包括:
- 函数的所有形参(即传入函数的参数)
- 没有实参,值为 undefined
- 函数声明
- 值为函数对象
- 如果变量对象存在相同属性名,则完全替换这个属性
- 变量声明
- 值为 undefined
- 如果变量对象跟已声明的形参或函数同名,则变量声明不会干扰已存在的这类属性( 注 ① )
function foo(a) {
var b = 2;
function c() {}
var d = function () {};
b = 3;
// ① 对 a b c 重新申明(不赋值),并不会对之前的变量有任何干扰
var a, b, c;
}
foo(1);
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
在进入执行上下文之后,AO 是:
AO = {
arguments: {
0:1,
length: 1
},
a:1,
b: undefined,
c: reference to function c() {},
d: undefined
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
因为进入执行上下文时,代码还没有执行,所以其中的声明的变量值都是 undefined,但声明的函数
会对应一个函数的引用,注意 var c = function () {}
这种不叫函数声明
但执行完代码之后,AO:
AO = {
arguments: {
0:1,
length: 1
},
b: 2,
c: reference to function c() {},
d: reference to FunctionExpression "d"
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
执行代码期间会修改变量对象的属性值
# 思考
console.log(foo);
function foo() {
console.log("foo");
}
var foo = 1;
1
2
3
4
5
6
7
2
3
4
5
6
7
AO = {
foo: referenceto function foo() {}
}
// 开始执行代码,先执行 console.log(foo) 所以会打印 函数
// 再往下执行,foo 被赋值为 1
// 补充: 这种属于全局变量对象,没有函数的形参
1
2
3
4
5
6
7
2
3
4
5
6
7
进入执行上下文,会首先处理函数声明,其次处理变量声明,如果变量名跟已经声明的形参或函数名相同,则不会有干扰。
因此会打印 函数 function foo () {}
# 个人总结
执行代码片段,先创建执行上下文,生成 变量对象(VO),此时上下文中的函数和变量不可访问, 进入执行上下文,激活变量对象为活动独享(AO),此时可以访问其中的函数和变量 按顺序执行代码,并且重新给 AO 中的变量或函数进行赋值操作。