zoukankan      html  css  js  c++  java
  • 浅谈JS的变量提升

         JS的解析机制,是JS的又一大重点知识点,在面试题中更经常出现,今天就来唠唠他们的原理。首先呢,我们在我们伟大的浏览器中,有个叫做JS解析器的东西,它专门用来读取JS,执行JS。一般情况是存在作用域就存在解析,那它是怎么运行的呢。首先呢,然后分成两大步骤。

    1 第一步叫做JS预解析,这一步骤实际上是一种准备工作把,在执行之前,它会先浏览整个代码,然后寻找三种东西。 1var  2 函数声明    我来分别解释一下。首先它会提取带var声明的变量,然后放到作用域中,但是不会提取变量的值,会先给他赋值为undefined。然后呢会找到函数声明(关于函数声明上个博文有说),然后会把整个函数包括内容放到作用域。函数的参数也会被放进去。

    所以我们可以知道在代码运行前,所有带var声明的变量都提前被赋值为undefined,找到所有有名字的函数所有的内容和参数,都提前被解析。

    2 第二步就是执行了,JS解析器会逐行解读代码,在这过程中,只有表达式可以修改预解析的值(比如 = + - * / % -- ++ ! 参数....()),其他的会跳过。

      下面来看个简单的栗子

        var a; 
    
        alert(a); //undefined 来自预解析
    
        a = 3;  //逐行解读代码到这句的时候,表达式修改了预解析中a的值。
    
        alert(a); //3

    这是个很简单的代码,我们按照解析机制来分析下:

    第一步,找函数声明和var,然后找到了 var a=1; 这一句,就把 a提取出来然后赋值为undefined。即此时 a=undefined。

    第二步,逐行解读代码, var a; 不是表达式,跳过,第一个alert(a);由第一步可知此时a=undefined, a = 3;  是表达式,并且更改了a的值,于是现在a=3,

    所以最后弹出的是3。

    下面再看个稍微复杂点的栗子。

    var a=1; 
    function fn1(){ 
      alert(a); 
      var a =2 ;
       alert(a);
    }
    fn1();
    alert(a);

    我们按照解析机制来分析下:
    1 预解析:找到带var的a和函数fn1(),放到全局作用域。

    全局仓库:

    a =undefined 
    function fn1(){ 
      alert(a);
      var a =2 ;
       alert(a);
    }

    2 运行,逐行解读代码

    var a=1;  // 将全局a的值改成1
    function fn1(){   // 这里不是能够产生改变的表达式,所以运行的时候直接跳过。
      alert(a);  // 先找局部变量a undefined
      var a =2 ;  // 更改了局部作用域里a的值
      alert(a); // 2 
    }
    fn1(); //  注意,当遇见函数的调用的时候,也会对函数进行预解析和运行两步操作。原理是相同的。只是作用域需要注意。
    alert(a);//    在全局作用域里找a.显然等于1
    函数的解析过程:

    1  预解析:有点需要注意,当函数有参数的时候,参数也会被预解析,根据var a =2 ;找到 a 放到局部仓库  且a =undefined (注意这和全局作用域里的不是一个a,因为作用域不同,所以互相是不影响的),

    所以此时是有两个作用域

    全局仓库:

    a =1function fn1(){ 
      alert(a);
      var a =2 ;
      alert(a);
    }

    函数里的局部仓库:

    a =undefined ;

    2  逐行解读函数里面的代码:当读取到这里 alert(a); 会优先在局部仓库里找a ,弹出未定义 ,当局部没有的时候 会去全局找

     解读到var a =2 ; 优先在局部仓库里找a,把值改为2;所以解读到函数里面的第二个 alert(a); 就等于2了,

    解读到最后一个alert(a),因为在全部环境里执行,所以会在全局仓库里找a;弹出1。

    这个栗子,比较长,认真看几遍,一定会会收获良多的。

    再看一个有参数存在的栗子:

    有参数函数:

    var a = 1;
    function fn1(a){   
      alert(a);  //  1  来自局部作用域参数a,而且这个值是全局作用域里面的a作为参数传进来的值。
      a = 2 ;      修改了局部作用域里的a的值
      var a;
      alert(a); //  2
    }
    fn1(a);
    alert(a); // 1 来自全局作用域

    这里全局的预解析就不用多说了,找到了整个函数,和 a =undefined  然后执行a =1 ,到函数调用的时候,开始了函数的解析.

    函数解析过程

    1 预解析的时候,也会找到参数a,并且把它放到局部作用域,赋值为a = undefined.

    2 逐行解读代码:会从参数开始解读 参数相当于局部变量 fn1(a); 这里把全局作用域里面的a的值,当作参数传入,注意只是把值传入,两个a是互不影响的

    不在同一个作用域。所以函数中先弹出1,后来局部作用域里的a值被改为2,就弹出了2 ,。最后一句弹出了a是1,可以发现函数里面优先更改的是局部作用域里面的a.(当在局部找不到a的时候,会去全局找 并且可以修改全局的值,所以大家可以实验把参数去掉,弹出的就是1 2 2 了)

    下面再看一种比较特殊的情况,

    1 就是在预解析过程中,遇到重名的情况,这个时候规律就是这样的,会优先留下有内容的,简单来说就是:当函数与变量重名,留下函数,当函数与函数重名,留下后面的函数。栗子就不举了,大家自己实验一下。

     2 当有多个作用域的时候 解析器 会先预解析第一块的作用域中的东西 然后运行完成以后 再去预解析下一块的作用域的东西 再运行,后面的作用域可以找到前面作用域留下的东西,反过来就不行。

    注意 if for等不是一个作用域。

       今天写的比较多,其实知识点原理都差不多,都是我平常学习的笔记总结,希望对大家有用。我是沐晴,下篇不见不散喔。

  • 相关阅读:
    Berkeley DB(五) 补充
    案例研究–亚马逊服务中断,数据库崩溃–我们恢复数据库且无数据损失
    源代码管理十诫
    menucool
    如何:使用變數視窗將變數加入封裝
    翻转句子中单词的顺序
    【科研论文】新型脉冲电子围栏网络化系统设计
    Java对泛型的支持(二)
    springmvc camel mybatis集成实例及分析
    xtrabackup全备方案,备份恢复全过程记录
  • 原文地址:https://www.cnblogs.com/moqing/p/5588606.html
Copyright © 2011-2022 走看看