• 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中对象的属性,然后才会向上顺着作用域链向上检查该属性。

  • 相关阅读:
    TouTiao开源项目 分析笔记19 问答内容
    TouTiao开源项目 分析笔记18 视频详情页面
    TouTiao开源项目 分析笔记17 新闻媒体专栏
    TouTiao开源项目 分析笔记16 新闻评论
    TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现
    TouTiao开源项目 分析笔记14 段子评论
    计算机专业大学课程学习路线图
    Windows GitLab使用全过程
    2017年最后一天
    生成6个1~33之间的随机整数,添加到集合,并遍历集合
  • 原文地址:https://www.cnblogs.com/chendc/p/7158031.html
Copyright © 2020-2023  润新知