zoukankan      html  css  js  c++  java
  • JavaScript解析机制之变量提升

    1、什么是预解析?

    在当前作用域下,JS 运行之前,会把带有 var 和 function 关键字的事先声明,并在内存中安排好。(这个过程也可以理解为变量提升)然后再从上到下执行 JS 语句(预解析只会发生在通过 var 定义的变量和 function 上)

    2、var 声明的变量

    使用 var 声明的变量预解析:告诉解析器知道有这个名字的存在并默认将该变量赋值 undefined ,如下:

    console.log(x); //undefined
    var x = 5;

    变量 x 虽然是在 console.log 后面定义的,但使用 var 申明的 x 会提前保存在内存中,并赋值 undefined ,然后再从上往下执行 JS 语句 。它的执行顺序类似于下面的结构:

    var x;
    console.log(x); //undefined
    x = 5;

    先声明了 x,x 没有定义所以赋值为 undefined ,输出的结果自然为 undefined,然后再给 x 赋值为 5

    需注意的是,如果变量声明没有使用 var,则不存在变量提升。如下:

    console.log(x); //error: x is not defined
    x = 5;

    x 没有使用 var 声明,所以报错找不到 x

    3、functin 声明的函数

    使用 function 声明函数的预解析:先告诉解析器这个函数名的存在,然后在告诉解析器这个函数名的函数体是什么,如下:

    console.log(f); 
    function f() {
      console.log("123");
    }

    声明函数会把整个函数都提升到最前面 ,所以浏览器中结果会输出整个函数,结果如下:

    function f() {
      console.log("123");
    }

    如果在一个函数作用域中声明一个变量 ,那么它也会提升到函数作用域的最上面,如下:

    var x = 5;
    function f() {
      console.log(x); // undefined
      var x = 2;
    }
    f();

    以上虽然全局作用域声明了一个变量 x,但函数里也声明了一个变量 x,所以会先查找函数里面是否有变量 x,如果有就不会在全局下查找了。函数里面的变量 x 会被提升到函数作用域的最前面 ,并且赋值为 undefined,所以输出结果为 undefined ,类似于如下结构:

    var x = 5;
    function f() {
      var x;
      console.log(x); // undefined
      x = 2;
    }
    f();

    函数的参数也可以理解为函数作用域的变量 ,如下:

    var x = 5;
    function f(x) {
      console.log(x); // undefined
    }
    f();
    console.log(x); // 5

    为函数 f 传递一个形参 x,由于函数在调用时没有传递实参(也就是说变量 x 没有赋值),所以为 undefined 。而在全局下输出 x 自然在全局下查找变量 x ,结果为 5

    4、函数优先

    变量声明和函数声明都会被提升,如果同一个作用域下声明的两个相同变量或相同函数,后一个会覆盖前一个,如下:

    var x = 5;
    var x = 10;
    console.log(x);   // 10
    function f() {
        console.log("xx");
    }
    function f() {
        console.log("yy");
    }
    f();   // yy

    但有一个需要注意的细节,如果声明的变量与函数名相同 ,那又会怎么覆盖呢?可以看如下例子:

    var f = 5;
    function f() {
      console.log("xx");
    }
    f(); // error: f is not a function

    JavaScript 中,函数的预解析优先级是要高于变量的预解析的。无论函数在什么位置声明,都优选把整个函数提升到最前面。所以上面的例子中,虽然函数 f 是在变量 f 下面定义的,但是在预解析时先解析函数 f,然后再解析变量 f,后面的变量 f 会把前面的函数 f 覆盖,最后 f 为 5 为数值类型,所以调用 f 时报错,f 不是一个函数。

    需要注意的是 ,如果变量 f 定义后没有赋值 ,那么函数 f 就不会被覆盖了,如下:

    var f;
    function f() {
      console.log("xx");
    }
    f(); // xx

    所以函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部


    掌握以上知识,我们看下面的例子 :

    console.log(x); // function x() {console.log(5);}
    var x = 2;
    console.log(x); // 2
    function x() {
      console.log(3);
    }
    console.log(x); // 2
    var x = 3;
    console.log(x); // 3
    function x() {
      console.log(5);
    }
    console.log(x); // 3
    x();    // error: x is not a function

    以上例子,两个函数 x 优先提升,所以第二个函数 x 覆盖第一个函数 x,然后两个变量 x 提升,由于变量 x 提升后为 undefined,所以第二个函数没有被覆盖,第一个输出 x 结果为第二个函数 function x(){console.log(5);}

    随后 x 被赋值为 2 ,所以第二个输出 x 结果为 2

    因为第一个函数 x 已经被提升到前面去了,所以第三个输出 x 结果还是 2

    随后为 x 赋值为 3,所以第四,第五输出 x 结果为 3。最后调用 x,x 因为是数值类型,所以会报错 x 不是一个函数

    我的博客也会同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=e1ebv5s48l8j

  • 相关阅读:
    Cocos2Dx for XNA类解析(1): CCApplication
    struts2动态调用action的方法
    python导出依赖包
    python 字符串split()方法
    struts2使用通配符调用action
    python3重新编译
    Jquery中html()、text()、val()的使用和区别
    Javascript写在<body>和<head>中的区别
    设计模式Design Pattern(2)单例模式
    设计模式Design Pattern(1)简介
  • 原文地址:https://www.cnblogs.com/Leophen/p/11395026.html
Copyright © 2011-2022 走看看