js之预编译

389 阅读2分钟

js运行过程分为三步:1. 语法分析 2. 预编译 3. 解释执行,预编译的产生总体来说的话就是变量提升,变量提升的过程某些情况下会导致变量在各个阶段值不同

GO、AO对象

在预编译过程之中全局上编译前会产生GO对象(也就是window对象去对对象去做判断以及存储数据),函数体中会产生AO对象去实现相同的功能,AO实现步骤如下图所示(那么全局时步骤也相同,只是少了不存在的参数部分)
函数体预编译四部曲

  • 创建AO对象
  • 找形参和变量声明
  • 实参形参统一
  • 在函数体里面找函数声明,值赋给函数体

脚本代码块script执行前与之相同就是少了相关参数的部分,输出使用对象时函数会在AO对象中去找,如果AO对象中没有那么会顺到GO对象中去找。


实例分析

看个案例解析一下上述过程

    function test() {
        console.log(b);
        if(a) {
            var b = 100;
        }
        console.log(b);
        c = 234;
        console.log(c);
    }
    var a;
    test();
    a = 10;
    console.log(a);
    console.log(c);

这边小编分解了一下预编译过程更加方便理解一点

// 过程分析
1.生成GO到test()执行生成AO
GO {
    a: undefined, // test执行还没到给a赋值
    test: function() {...}
}
AO {
    b: undefined // 解释一下这一步虽然这边对b的声明实在条件语句之中,但预编译过程是这一步还是会被提升到最前面
},
2. 函数执行完
// c=234函数内部直接赋值相当于直接定义在GO之中
// a 为undefined所以if语句没有实现
GO {
   a: 10, 
   test: function() {...},
   c: 234
}
AO {
    b: undefined
}
// 所以函数最后输出为: undefined、undefined、234、10、234

特例:非匿名立即执行函数

因为当 JS 解释器在遇到非匿名的立即执行函数时,会创建一个辅助的特定对象,然后将函数名称作为这个对象的属性,因此函数内部才可以访问到该对象属性,但是这个值又是只读的,所以对它的赋值并不生效,所以打印的结果还是这个函数,并且外部的值也没有发生更改。

var foo = 1;
(function foo() {
    foo = 10;
    console.log(foo); // 输出foo这个函数
}())

当然我们只用let块级声明去定义变量时由于产生暂时性死区就不会有这一类变量提升的问题

end...