为了帮助大家进更加深刻的理解变量对象,我们结合一些简单的例子来进行探讨。
// demo01
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
运行test函数时,对应的上下文开始创建,我们用如下形式来表达这个过程。
// 创建过程
testEC = {
VO: {}, // 变量对象
scopeChain: [], // 作用域链
this: {}
}
// 这里暂时不深入作用域链与this,后续章节详解
// VO为Variable Object的缩写,即变量对象
VO = {
arguments: {...},
foo: <foo reference>,
a: undefined
}
相信大家应该还记得函数调用栈。如果当前执行上下文处于函数调用栈的栈顶时,也就意味着当前上下文处于激活状态。此时变量对象称之为活动对象(AO,Activation Object)。活动对象中包含变量对象中的所有属性,并且此时所有的属性都已经完成了赋值,除此之外,活动对象还包含了this的指向。
// 执行阶段
VO -> AO
AO = {
arguments: {},
foo: <foo reference>,
a: 1,
this: Window
}
我们可以通过断点调试的方式在chrome开发者工具查看这一过程。如何使用断点调试会在后续章节详解。
因此上面的例子实际执行顺序如下:
function test() {
function foo() {
return 2;
}
var a = undefined;
console.log(a);
console.log(foo());
a = 1;
}
test();
可以再来一个例子,巩固一下我们的学习。
// demo2
function test() {
console.log(foo);
console.log(bar);
var foo = 'Hello';
console.log(foo);
var bar = function () {
return 'world';
}
function foo() {
return 'hello';
}
}
test();
// 创建阶段
VO = {
arguments: {...},
foo: <foo reference>,
bar: undefined
}
// 这里有一个需要注意的地方,因为var声明的变量当遇到同名的属性时,会跳过而不会覆盖
// 执行阶段
VO -> AO
VO = {
arguments: {...},
foo: 'Hello',
bar: <bar reference>,
this: Window
}
需要结合上面的知识点,仔细对比这个例子中变量对象的变化过程,如果你已经理解了,说明变量对象相关的东西已经难不倒你了。
最后留下一个简单的思考题:下面的例子中,函数bar的变量对象中是否包含了变量a?
function foo() {
var a = 20;
function bar() {
a = 30;
console.log(a);
}
bar();
}
foo();