深入理解JavaScript中的变量对象(Variable Object)

525 2 年前
在javascript实际编程中,声明变量和函数是件平常的事,那么解释器在每个执行上下文是如何查找和访问这些声明的变量和函数的呢,这个就要靠变量对象(variable object)了

变量对象(Variable Object缩写为VO)是一个与执行上下文相关的特殊对象,存储了在上下文中定义的变量和函数声明

在具体实现层面(以及规范中)变量对象只是一个抽象概念。

对于所有类型的执行上下文来说,变量对象的一些操作(如变量初始化)和行为都是共通的。从这个角度来看,把变量对象作为抽象的基本事物来理解更为容易。同样在函数上下文中也定义和变量对象相关的额外内容。

变量对象(Variable object)是说JS的执行上下文中都有个对象用来存放执行上下文中可被访问但是不能被delete的函数标示符、形参、变量声明等。它们会被挂在这个对象上,对象的属性对应它们的名字对象属性的值对应它们的值但这个对象是规范上或者说是引擎实现上的不可在JS环境中访问到活动对象

激活对象(Activation object)有了变量对象存每个上下文中的东西,但是它什么时候能被访问到呢?就是每进入一个执行上下文时,这个执行上下文儿中的变量对象就被激活,也就是该上下文中的函数标示符、形参、变量声明等就可以被访问到了

抽象变量对象VO (变量初始化过程的一般行为)
  ║
  ╠══> 全局上下文变量对象GlobalContextVO
  ║        (VO === this === global)
  ║
  ╚══> 函数上下文变量对象FunctionContextVO
           (VO === AO, 并且添加了<arguments>和<formal parameters>)

全局上下文中的变量对象

全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象; 这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。

全局上下文中的变量对象——在这里,变量对象就是全局对象自己:

全局对象初始创建阶段将Math、String、Date、parseInt作为自身属性,等属性初始化,同样也可以有额外创建的其它对象作为属性(其可以指向到全局对象自身)。

当访问全局对象的属性时通常会忽略掉前缀,这是因为全局对象是不能通过名称直接访问的。不过我们依然可以通过全局上下文的this来访问全局对象,同样也可以递归引用自身。

String(10); // 就是global.String(10);

// 带有前缀
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;

全局上下文中的变量对象就是全局对象自己

VO(globalContext) === global;

函数上下文中的变量对象

在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。

活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object 呐,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。

活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。

函数上下文建立的细节

1、查找调用函数的代码。

2、执行代码之前,先进入创建上下文阶段:

- 初始化作用域链
- 创建变量对象:
	+ 创建arguments对象,检查上下文,初始化参数名称和值并创建引用的复制。
	+ 扫描上下文的函数声明(而非函数表达式):
		+ 为发现的每一个函数,在变量对象上创建一个属性——确切的说是函数的名字——其有一个指向函数在内存中的引用。
		+ 如果函数的名字已经存在,引用指针将被重写。
	+ 扫描上下文的变量声明:
		+ 为发现的每个变量声明,在变量对象上创建一个属性——就是变量的名字,并且将变量的值初始化为undefined
		+ 如果变量的名字已经在变量对象里存在,将不会进行任何操作并继续扫描。
	+ 求出上下文内部“this”的值。

3、激活/代码执行阶段:

在当前上下文上运行/解释函数代码,并随着代码一行行执行指派变量的值。

从上面函数上下文建立的步骤可以看出 变量对象 对应在第二步

function foo(i){
    var a = 'hello'
    var b = function(){}
    function c(){}
}
foo(22)

当我们调用 foo(22)的时候,整个创建如下:

ECObj = {
    scopChain: {...},
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: undefined,
        b: undefined
    },
    this: {...}
}

当进行到第三步激活/代码执行阶段时:

ECObj = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: 'hello',
        b: pointer to function privateB()
    },
    this: { ... }
}

函数的形参(当进入函数执行上下文时) —— 变量对象的一个属性,其属性名就是形参的名字,其值就是实参的值;对于没有传递的参数,其值为undefined

函数声明(FunctionDeclaration, FD) —— 变量对象的一个属性,其属性名和值都是函数对象创建出来的;如果变量对象已经包含了相同名字的属性,则替换它的值

变量声明(var,VariableDeclaration) —— 变量对象的一个属性,其属性名即为变量名,其值为undefined;如果变量名和已经声明的函数名或者函数的参数名相同,则不会影响已经存在的属性。

分类栏目
© 2018邮箱:11407215#qq.comGitHub沪ICP备12039518号-6