预编译与变量提高详细讲解

前言:做为解释性语言的JS,在执行前会先进行预编译。理解了预编译,JS中的变量提高就能够更快的搞懂啦!javascript

1、预编译前奏

在讲解预编译前,先讲解个知识点。敲黑板啦!小伙伴们不要光看,本身打打代码才知道。java

  1. imply global:暗示全局变量:若是变量未经声明就赋值,此变量归全局对象全部,即归window全部。以下所示,即window.a = 10

这个window究竟是什么,其实它就是一个全局对象,提供了一个全局做用域。在全局做用域声明变量,就是为全局对象添加属性。即在window对象中添加了一个属性a,其值为10。bash

<script type="text/javascript">
    a = 10;
    console.log(a);//访问这个a实际上就是访问window.a
<script>
复制代码

为了更好地理解,再来看一下下面代码。 window.a和window.b会输出什么呢?

<script type="text/javascript">
 function test() {
    var a = b = 123;
 }
 test();
<script>
复制代码

步骤:

  • b = 123;b是没有声明就赋值的,因此根据上面的imply global暗示全局变量,是一个全局变量,是window的一个属性,因此能够经过window.变量名访问
  • var a = b;由于它在函数里,不是全局变量,是局部变量,因此在window中没有。
  1. 一切声明的全局变量,全是window的属性。
<script type="text/javascript">
    var b = 20;
    console.log(b);
<script>
复制代码

1、预编译

看一段代码函数

  • b输出的结果的undefined,那么undefined是什么意思呢?是var声明了变量可是未对它进行初始化。因此先让这个变量初始化为undefined,也就是先让b=undefined直到真正赋值阶段时才是20。
  • 而a输出的是Uncaught ReferenceError: Cannot access 'a' before initialization。翻译过来的意思就是没法在初始化以前访问a。
<script type="text/javascript">
    //var
    console.log(b);//undefined
    var b = 20;
    //let
    console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization
    let a = 10;
<script>
复制代码

为何varlet二者不同呢?先来说讲varui

  • var b = 20;包括两个步骤:
  1. 声明:var b;
  2. 赋值:b = 20;
<script type="text/javascript">
var b;//该声明提高了,提高到做用域的顶部。
console.log(b);//undefined
b = 20;
<script>
复制代码

var的提高实际上是建立初始化都提高了。也许你又会问那为何初始化的值是undefined。这和执行前发生的预编译有关。spa

  • 此提高过程在预编译中进行:
  1. 函数:预编译发生在函数执行前一刻
  2. 整个js :预编译发生在全局执行的前一刻

函数预编译步骤

先看下面的代码到底输出什么?既有函数又有变量,并且还同名,那变量的声明提高和函数提高到底谁的优先级高呢?想必是很懵逼吧,不要紧,跟着我一块儿来一步一步理解下。翻译

function test(a) 
 {
         console.log(a);
         var a = 123;
         console.log(a);
         function a() {}
         console.log(a);
         var b = function () {}
         console.log(b);
         function d() {}
 }
     test(1);
复制代码
  1. 建立AO对象:
AO{}
复制代码
  1. 找到形参和变量声明,将形参和变量声明做为AO的属性名,属性值为undefined:
AO{
    形参或变量声明:undefined
}
也就是
AO{
    a:undefined,//这里形参function test(a)和变量var a都是,写一次就行了
    b:undefined
}
复制代码
  1. 找到实参的值,赋值给形参
AO{
    形参或变量声明:实参
}
也就是
AO{
    a:1,
    b:undefined
}
复制代码
  1. 函数体里面找到函数声明, 值为函数体
AO{
    形参或变量声明:函数体
}
也就是
AO{
    a:function a() {},//函数声明
    b:undefined,
    //注意了 var b = function () {}这个不是函数声明,是函数表达式,因此b的值仍是undefined
    d:function d() {}//函数声明
}
复制代码
  • 此时AO对象已经建立完,那么就开始执行函数,开始打印。

注意:要打印的值都从预编译后的AO对象中取值code

function test(a) 
 {      
    //1.输出function a() {}
         console.log(a);
    /*2. 预编译时已经变量提高了,可是赋值尚未操做。因此此时AO对象中a的值改变为123
        AO{
        a:123,
        b:undefined,
        d:function d() {}
    }
    */
         a = 123;
    //3.输出:123
         console.log(a);
         function a() {}
    //4.输出123
         console.log(a);
    /*5.改变AO对象中b的值
    AO{
        a:123,
        b:function () {},
        d:function d() {}
    }
    */
         var b = function () {}
    //6.输出function () {}
         console.log(b);
         function d() {}
 }
复制代码

实际输出结果和分析的同样。棒棒哒!cdn

3、提高

理解了预编译后,想必var的变量提高弄明白了吧!var提高指的是建立初始化都被提高了。因此下面代码输出的结果才是undefined对象

console.log(a);
var a = 20;
复制代码
console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;
复制代码

对于上面的结果也许你会说:那let不就没有提高。这就错了,其实它是有提高的,只不过提高的只有建立过程,初始化时没有提高的。看码

let a = 1;
         {
             console.log(a);
             let a = 2;
         }
复制代码

若是let不存在提高,那么应该输出1。可是却报错 Uncaught ReferenceError: Cannot access 'a' before initialization。这就说明let存在提高。只是被存在“暂存死区”中。只有当初始化后才能被引用。

4、总结

  1. var :【建立】和【初始化】都被提高
  2. function:总体提高,也就是【建立】【初始化】【赋值】都被提高
  3. let:【建立】过程提高,但初始化没有提高。
相关文章
相关标签/搜索