zoukankan      html  css  js  c++  java
  • js变量作用域--变量提升

    1、JS作用域

    在ES5中,js只有两种形式的作用域:全局作用域和函数作用域,在ES6中,新增了一个块级作用域(最近的大括号涵盖的范围),但是仅限于let方式申明的变量。

    2、变量声明

    1 var x;      //变量声明
    2 var x=1;    //变量声明并赋值
    3 x = 1;    // 定义全局变量并赋值

    3、函数声明

    function fn(){};     //函数声明并定义
    var fn = function(){};    // 实际上是定义了一个局部变量fn和一个匿名函数,然后把这个匿名函数赋值给了fn

    4、变量提升

    var tmp = new Date();
    function fn(){
        console.log(tmp);  //Wed Jul 12 2017 22:11:56 GMT+0800 (中国标准时间)
    }
    fn();

    a情形

    var tmp = new Date();
    function fn(){
        console.log(tmp);    //undefined
        if(false){
            var tmp = 'hello';
        }
    }
    fn();

    b情形

    var tmp = new Date();
    function fn(){
        console.log(tmp);    //undefined
        if(true){
            var tmp = 'hello';
        }
    }
    fn();

    c情形

    从上面可以看到,b情形和c情形为什么不同于a情形,就是因为变量提升了(ps: c情形不同于b情形的是判断条件为true,但是这里不是看代码有没有被执行,是看变量有没有被定义)。fn函数里面定义了同名变量tmp,无论在函数的任何位置定义tmp变量,它都将被提升到函数的最顶部。等同于下面情形:

    var tmp = new Date();
    console.log(tmp);
    function fn(){
            var tmp;
        console.log(tmp);    //undefined
        if(false){
            var tmp = 'hello';
        }
    }
    fn();

    这里需要说明的是,虽然所有的申明(包括ES5的var、function,和ES6的function *、let、const、class)都会被提升,但是var、function、function *和let、const、class的的提升却并不相同!具体原因可以看这里的说明(大体的意思是虽然let,const,class也被提升了,但是却并不会被初始化,这时候去访问他们则会报ReferenceError异常,他们需要到语句执行的时候才会被初始化,而在被初始化之前的状态叫做temporal dead zone)。

    因为这样的原因,推荐的做法是在申明变量的时候,将所用的变量都写在作用域(全局作用域或函数作用域)的最顶上,这样代码看起来就会更清晰,更容易看出来那个变量是来自函数作用域的,哪个又是来自作用域链。

    5、重复声明

    var x = 1;
    console.log(x);
    if(true){
        var x = 2;
        console.log(x);
    }
    console.log(x);

    上面的输出其实是:1 2 2。虽然看起来里面x申明了两次,但上面说了,js的var变量只有全局作用域和函数作用域两种,且申明会被提升,因此实际上x只会在最顶上开始的地方申明一次,var x=2的申明会被忽略,仅用于赋值。也就是说上面的代码实际上跟下面是一致的:

    var x = 1;
    console.log(x);
    if(true){
        x = 2;
        console.log(x);
    }
    console.log(x);

    6、函数和变量同时提升的问题

    console.log(fn);
    function fn(){};
    var fn = 'string';

    上面的输出结果其实是: function fn(){} ,也就是函数内容。

    console.log(fn);
    var fn = function fn(){};
    var fn = 'string';

    这时输出结果就是undefined,知道上面的声明提升的道理就不难理解了。

    总结:

    要彻底理解JS的作用域和Hoisting,只要记住以下三点即可:

          1、所有申明都会被提升到作用域的最顶上

          2、同一个变量申明只进行一次,并且因此其他申明都会被忽略

          3、函数声明的优先级优于变量申明,且函数声明会连带定义一起被提升

    注意:

    通过with语句,可以临时改变运行期上下文的作用域链,此时的对非var定义的变量进行访问,会首先访问with中对象的属性,然后才会向上顺着作用域链向上检查该属性。

  • 相关阅读:
    HDU 1010 Tempter of the Bone(DFS剪枝)
    HDU 1013 Digital Roots(九余数定理)
    HDU 2680 Choose the best route(反向建图最短路)
    HDU 1596 find the safest road(最短路)
    HDU 2072 单词数
    HDU 3790 最短路径问题 (dijkstra)
    HDU 1018 Big Number
    HDU 1042 N!
    NYOJ 117 求逆序数 (树状数组)
    20.QT文本文件读写
  • 原文地址:https://www.cnblogs.com/chendc/p/7158031.html
Copyright © 2011-2022 走看看