前面的话
前端日问,巩固基础,不打烊!!!
解答
先来说几个概念:
-
变量对象:保存了执行上下文中的变量和函数声明。
-
在全局执行上下文中,变量对象就是全局对象(一般指window对象)。
在。 -
在函数执行上下文中,变量对象用AO表示。
函数执行上下文中的AO
函数的执行上下文在调用时被创建,创建之后分为两个阶段:进入执行上下文 和执行代码。这两个过程的AO会有一定的差异,下面通过例子来分析:
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
当执行foo
函数时,会创建相应的执行上下文。
-
进入执行上下文
这时的AO包括:
arguments对象
,函数的所有形参
(如果有实参就为实参的值)、函数声明
、变量声明
。AO = { arguments: { 0:1, length: 1 }, a: 1, b: undefined, c: reference to function c(){}, d: undefined }
-
执行代码
在代码执行阶段,会顺序执行代码,然后改变AO对象中的值,当代码执行完毕之后,对应的AO会变为:
AO = {
arguments: {
0:1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
总结
- 全局执行上下文的变量对象就是全局对象
- 函数执行上下文的变量对象是AO;进入函数执行上下文时,AO会包括
arguments
对象,函数形参(如果有实参则为实参值,否则为undefined)、函数声明、变量声明(undefined); 代码执行时,根据代码的顺序修改对应的属性值。
作用域链
定义:由多个执行上下文的变量对象构成的链表。
通俗来说,比如一个函数里面,访问了一个变量,先从其AO对象中找,如果没有的话就从其父级的中的AO对象中找,最终找到全局对象。
函数创建时:
函数有一个内部属性[[scope]], 当函数创建时,就会保存所有父级的变量对象到其中。
举个例子:
function foo() {
function bar() {
...
}
}
函数创建时,各自的[[scope]]为:
foo.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
函数被调用时
当函数被调用时,会创建函数执行上下文,将自身的AO对象添加到作用域链的前端。
此时再来看一下上面两个函数的作用域链:
foo.[[scope]] = [
AO,
globalContext.VO
];
bar.[[scope]] = [
AO,
fooContext.AO,
globalContext.VO
];
参考文章: