【JS】五分钟理解预编译

我们都明白js在运行中会进行三个步骤,1.整体语法分析(排除基本错误),2.进行预编译,3.解释执行(按照顺序解释一行执行一行)。那么所谓的预编译是什么呢?

先看两个基本例子

console.log(a);
var a =123;
console.log(a);

//undefined
//123
test();
function test(){
  console.log('a');
}

// a

我们可以发现这两个例子均未报错,这就是预编译的作用,他会使得函数声明整体提升(即函数无论写在第几行都会被提升至逻辑的最前面),同时变量声明会提升(即把上题中的var a 提升至最前面)

但光光了解这些并不能解决所有问题,我们看下面的例子

function a(a){
  var a =a+1;
  console.log(a);
}
var a;
a(1);
console.log(a);

利用两个提升显然无法解决这个问题,这就要求我们深入的学习预编译,因为它的存在本就是用于解决执行顺序问题,我在之前作用域和作用域链里提到了两个词 Activation Object 和 Global Object (以下简称AO 和GO),我们来分析上述问题。

在执行完js第一步语法分析后,我们进入预编译环节,全局预编译第一步,生成一个GO对象(其实就是window),第二步,找变量声明,并将变量作为GO对象的一个属性名,值为undefined 第三步 找函数声明,并将函数名作为GO对象的属性名,值为函数体 我们会发现,GO对象中a属性的值从undefined转化成了a函数的函数体。也就是说第二个console输出的结果是

ƒ a(a) {
        a = a + 1;
        console.log(a);
    }
而局部函数的预编译发生在函数执行前的一刻,第一步我们会创建一个AO对象,第二步找形参和变量声明,同样将它们作为AO对象的属性名,值为undefined 第三步,将实参的值传到AO对象中,作为形参属性的值,第四步在函数体内找函数声明,值赋予函数体。
AO={                  AO={
  a :undefined; =>    a:1
}                     }

a值为1,进行js解释执行后,a值为2,也就是第一个console的结果。

这就是全局的预编译和局部的预编译,熟悉之后我们可以解决一切关于执行顺序的问题。值得注意的是,在局部访问一个变量时,当AO中不存在此属性名,需要往GO中寻找。

function a() {
        console.log(a);
    }
var a;
a();
console.log(a);
// ƒ a() {
//     console.log(a);
// }
// ƒ a() {
//     console.log(a);
// }




猜你喜欢

转载自blog.csdn.net/timcope/article/details/80190004