活动对象与词法分析

407 阅读2分钟

今天小伙伴们给出了一道这样的题目:

var a = 1;
function fn1(a) {
    alert(a);
    var a = 2;
}
fn1(a);
alert(a)

正确答案是1,1,小伙伴对于答案很疑惑,考虑函数fn1内部变量提升,为什么不是undefined,1。好,那咱今天就来分析一下这个问题。

首先,我们先来学习一些知识点

变量对象

参考链接 ECMA-262-3 in detail. Chapter 2. Variable object.

如果变量与执行上下文相关,并且变量知道它的值存在哪,怎么访问,那么这种机制叫做变量对象(VO)。 全局的变量对象始终存在,是在进入任何执行上下文之前就创建的对象。这个对象只有一个,并且在任何地方都可以访问,它的销毁与程序的终止同时发生。 而函数的局部环境的变量对象,则是在函数执行的过程中存在,也就是在函数执行之前创建的。 VO是不能直接访问的,要访问的话,其实就需要一个与VO相对应的AO即活动对象。这个VO感觉就是一个抽象的东西,真正搞事情的还是AO。那么这个AO是怎么创建的呢?此时就用到了JS的词法分析。

词法分析的步骤就是: 1、生成一个活动对象AO 2、分析形参 3、分析变量的声明 4、分析函数的声明

我们按照这个步骤来走一下上面的代码, 第一步,生成AO对象,为一个空对象 AO = {} 第二步,分析形参,形参为a,即 AO.a = undefined , 但是实参为1, 即AO.a = 1 第三步,分析变量声明,通过var声明了a,但是此时AO中存在a属性,放弃修改,a属性的值依然为1。如果变量声明的属性在AO中不存在,即AO.x = undefined 第四步,分析函数声明,没有,结束。

因此,结果就是1,1

那么我们将代码修改一下:

var a = 1;
function fn1(a) {
    alert(a);
    function a(){};
    var a = 2;
    alert(a)
}
fn1(a);
alert(a)

此时,第四步中发现了函数声明a,那么此时函数声明覆盖AO中的a属性,编程AO.a = function a(){} 答案就是 function a(){},2,1

变量对象应该是es3时代的产物,在es6+时代,你使用let已经无法同时声明同一个变量了。