JS预编译详解

咱们都知道javascript是解释型语言,执行的特色呢是编译一行,执行一行。按照这个思路有时候咱们在运行代码时会有一些使人费解的现象出现。下面咱们一块儿来执行下面三段代码。javascript

<script>
        var a = 123;
        console.log(a);
</script>

  

<script>
        console.log(a);
</script>

  

<script>
        console.log(a);
        var a = 123;
</script>

 

运行上面三段代码能够得出结果分别为:  123. 和 a is not defined. 和 undefined.html

按理说第三个代码应该也输出:a is not defined.可是并无,这正是因为javascript在执行代码前的预编译产生的。java

前面说的是变量的例子,下面咱们看看函数执行时的效果。请看下面两段代码。函数

<script>
    function test() {
        document.write("a");
    }
    test();
</script>

  

<script>
    test();
    function test() {
        document.write("a");
    }
</script>

  

运行上面两段代码执行结果都为:a。 这也是预编译所产生的结果。spa

这又是为何呢。下面咱们带着疑问来了解一下预编译究竟是什么。code

全局变量、暗示全局变量和局部变量的概念

在这以前咱们先来了解一下什么是:暗示全局变量。咱们都知道变量有两种类型:全局变量和局部变量。在函数外部定义的就叫全局变量,在函数内部定义的就叫局部变量。htm

先看一段代码对象

<script>
        var a = 1;
        b = 2;           
        function test() {
            var c = d = 3;      //变量的赋值是从右到左的,至关于:d = 3; var c = d;
            console.log(c);
            console.log(d);
        }
        test();
        console.log(b);
        console.log(d);
</script>

  

执行结果为:3  3  2  3.  blog

奇怪,d 不是局部变量吗,为何能够在全局进行访问?  为何 b = 2 这样也能够声明变量 ?ip

那么就只有一个可能,d 不是局部变量,而是暗示全局变量。

接下来咱们熟悉一下两个重要的概念。

  1. imply global暗示全局变量:即任何变量,若是变量未声明就赋值,此变量为全局对象(window)全部。window就是全局的域。
  2. 任何声明的全局变量,皆为window的属性。因此访问一个全局变量能够console.log(a),也能够console.log(window.a).

因此出现上面的结果也就不难解释了,a 是全局变量。b d两者是暗示全局变量(即未声明就赋值的变量)。c 是局部变量。因此变量d能够在外部进行访问。之后咱们只要记得暗示全局变量也是全局变量便可。

JS运行三部曲

下面,咱们正式进入JS运行三部曲部分。

一、语法分析

二、预编译

三、解释执行

在执行代码前还有两个步骤,即语法分析和预编译。

在预编译以前,系统会对整个代码所有扫描一遍,看看有没有低级的语法错误,如少了括号或引号等等。解释执行就是从上到下依次执行函数代码。

下面咱们重点来说一下预编译。讲完预编译后相信你们就会知道以前的代码执行结果为何是那样的啦。

咱们分函数体内的预编译和全局预编译两个部分来说解预编译。

函数体内的预编译

函数体内预编译四部曲    //发生在函数执行的前一刻。

  1. 建立AO对象。Activation object。即做用域,或叫执行期上下文。
  2. 找形参和变量声明,将变量和形参名做为AO的属性名,初始值为undefined.
  3. 将形参和实参相统一。
  4. 在函数体里找函数声明,值(函数声明)赋予AO对象。

注意:预编译过程只涉及变量或函数的声明,赋值语句在解释执行的阶段才进行。

咱们来看一下这段代码具体解释一下函数体内预编译的详细过程,也能够本身先执行一遍,看一下结果与本身预期的是否一致。

<script>
        function fn(a){        //定义一个函数fn()
            console.log(a);
            var a = 123;
            console.log(a);
            function a() {}    //函数的声明在预编译时已经被识别,因此在调用fn(1)时,忽略这条语句
            console.log(a);
            var b  =function () {}  
            console.log(b);
            function d() {}
        }
        fn(1);
</script>

  一、建立一个AO对象。  AO {  }。

  二、找形参和变量声明,将变量和形参名做为AO的属性名,初始值为undefined。变量名和形参名一致的话只写一个就行。

    AO { a : undefined

        b : undefined

         }

  三、将实参和形参统一。

    AO {a : 1

      b : undefined

      }

  四、在函数体里找函数声明,值(函数声明)赋予AO对象。

    AO {a : function a() {}

      b : undefined

      d : function d() {}

      }

预编译结束后,才执行fn(1)函数。(执行过程为从上到下依次执行)

执行结果为:function a() {} 、12三、12三、function () {}.

 全局预编译

全局预编译(整个script代码块执行前)

  一、建立一个GO对象。GO对象即window对象。

  二、找变量声明。初始值为undefined。注:全局预编译没有形参和实参

  三、找‘全局里的函数声明,函数声明赋值到全局里的GO。

咱们来看一下下面这段代码来了解一下全局预编译。

<script>

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

  一、建立一个GO对象。 GO { }

  二、找变量声明  GO {a : undefined}

  三、找函数声明  GO {a : undefined

             b : function b(a){...} }

从上到下执行整个代码。

执行到 b (1)时,先进行函数体的预编译,而后再执行 b (1)函数。

  一、建立一个AO对象   AO { }

  二、找形参和变量声明  AO {a : undefined}

  三、将实参和形参统一  AO {a : 1}

  四、在函数体里找函数声明,值(函数声明)赋予AO对象。没有函数体,则这一步忽略。

预编译完成后执行 b(1)函数。

最后显示的结果为:1  undefined  2.

总结:JS运行过程

全局预编译(脚本代码块script执行前) 

  1. 查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名做全局对象的属性,值为undefined  查找函数声明,函数名做为全局对象的属性,值为函数引用
  2.  查找函数声明,函数名做为全局对象的属性,值为函数引用。

从上往下依次执行代码,遇到函数体时,进行函数体预编译。

函数体预编译(函数执行前)

  1. 建立AO对象(Active Object) 
  2. 查找函数形参及函数内变量声明,形参名及变量名做为AO对象的属性,值为undefined 
  3. 实参形参相统一,实参值赋给形参 
  4. 查找函数声明,函数名做为AO对象的属性,值为函数引用

函数体预编译后,继续往下执行代码。

相关文章
相关标签/搜索