为了帮助大家进更加深刻的理解变量对象,我们结合一些简单的例子来进行探讨。

// 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();

results matching ""

    No results matching ""