js中的预编译?

339 阅读5分钟

学习JavaScript语言的人都知道,这门语言是解释性语言,那既然是解释型语言(编译一行,执行一行),那又何来预编译一说呢? 在我们进入正题之前,我们先来看看什么叫解释性语言,什么叫编译型语言?

编译型语言:

使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。

特点: 在编译型语言程序执行之前,由于计算机不能识别高级语言,需要一个专门的编译过程,把高级语言程序编译成计算机能读懂的机器语言的文件,比如.exe文件,在编译完之后,以后要在运行时,就不需要再去编译了,只要执行.exe文件即可。这些都是在执行之前就做完了的。

解释型语言:

使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是代码在执行时才被解释器一行行动态翻译和执行,而不是在执行之前就完成翻译。

特点: 在执行解释性语言程序的时候,不需要事先编译,而是将代码解释成机器码并立即执行,也就是说一边解释一边执行。

JavaScript运行三部曲:

  1. 语法分析 就是引擎检查代码是否存在低级语法错误
  2. 预编译 简单的来说就是给变量和函数开辟一些内存空间,存放这些变量和函数(对于js是有变量提升和函数提升的)
  3. 解释执行 顾名思义就是执行代码咯

预编译 发生在函数执行之前, 四部曲

  1. 创建AO对象(activationn object)
  2. 找形参和变量声明,将形参和变量声明作为AO对象的属性名,值为undefined
  3. 将实参和形参统一
  4. 在函数体里找函数声明,将函数名作为AO的属性名,值赋予函数体

预编译也发生在全局,三部曲

  1. 创建GO对象
  2. 找变量声明,将变量声明作为GO对象的属性名,值赋予undefined
  3. 找全局局里的函数声明,将函数名作为GO的属性名,值赋予函数体

下面我们用三部曲和四部曲来解释分析一下下面的程序:

<script>
    var a=1
    function fn(a){
    var a=2
    function a(){}
    var b=a
    console.log(a);
        }
        fn(3)
</script>

首先,在这个程序中有函数体和全局变量。那么我们就要使用上三部曲和四部曲

分析一下这个程序:

  1. 页面产生便创建GO全局对象(Global Object)(也就是window对象);
  2. 第一个脚本文件加载;
  3. 脚本加载完毕后,分析语法是否合法;
  4. 开始预编译 我们来抽象一下:马上创建一个GO对象
预编译:
GO={
   a undefined
   fn  function(a){ var a=2; function a(){}; var b=a;console.log(a);}
}

执行fn(3)函数之前,发生预编译生成AO对象(对函数体内的)执行四部曲内的第二步:找形参和变量声明,将形参和变量声明作为AO对象的属性名,值为undefined

AO={
    a undefined
    b undefined
    

然后执行四部曲第三步:将实参和形参统一(也就是把实参的值赋给形参)

AO={
    a undefined=>3(由undefined变成了3)
    b undefined
    }

四部曲第四步:在函数体里找函数声明,将函数名作为AO的属性名,值赋予函数体

AO={
    a undefined=>3=>function(){}
    b undefined
    }

函数体预编译已完成,就执行了:(变量赋值是在执行期间完成) 首先我们看GO对象,var a=1;在这时是不是就把1赋值给了a。

GO={

    a undefined=>1
    fn funcyion(){}
    }
    
    
AO={
一步步执行:
    a undefined=>3=>function(){}=>2
    b undefined=>2(题目中把a赋值给了b,a的值为2)然后执行console.log(a)输出2
  

(这个稍微好理解一点)下面我们来看看一个函数体的预编译

function fn(a) {
      console.log(a);  //function
      var a = 123;
      console.log(a);   //123
      function a() {}
      console.log(a);   //123
      var b = function() {}
      console.log(b);   //function
      function d() {}
      var d = a
      console.log(d);   //123
    }
    fn(1)

首先执行四部曲第一步:创建AO对象,然后第二部,找形参和变量声明(并声明undefined)。第三步:将形参的值赋给实参。第四步,找函数声明,,将函数名作为AO的属性名,值赋予函数体(注意:函数声明和函数表达式不一样 var a=function()这是一个函数表达式,a=function()相当于赋值了一个函数体给a)

第一步:AO={
    
}

第二步:(在第一步的基础上)
AO={a既是形参也是变量,但只要声明一个
    a undefined
    b undefined
    d undefined
    
}
第三步:
AO={a既是形参也是变量,但只要声明一个
    a undefined=>1
    b undefined
    d undefined
    
}
第四步:
AO={a既是形参也是变量,但只要声明一个
    a undefined=>1=>function(){}
    b undefined
    d undefined=>function(){}
    
}

我们的预编译就完成了。开始执行:

先找到fn(1);进入函数体fn(),函数从上往下执行,第一步输出a。我们看到预编译的最后样子,a是不是最后是一个函数体,所以a输出function。然后给a赋值123(看下图)。所以下一个console.log(a)输出123,再继续function a() {}这个我们就不需要看了,继续往下console.log(a)这是我们继续往预编译的赋值后的AO里看,a还是123,所以输出123。再继续执行var b = function() {},这是一个函数表达式,在这里把函数体赋值给了b,console.log(b);就输出function。function d() {}也不用看了。继续往下执行var d = a这时把a赋给了b,我们看向预编译的a是不是为123,所以把123赋值给b。那么console.log(d);是不是就输出123**.**

AO={a既是形参也是变量,但只要声明一个
    a undefined=>1=>function(){}=>123
    b undefined =>function(){}
    d undefined=>function(){}=>123
    
}

注:有错请指出一下谢谢!互相成长