• 下一篇: + name +

    es6块级作用域

    发布时间:2019-08-10 发布网站:脚本宝典
    脚本宝典收集整理的这篇文章主要介绍了es6块级作用域脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

    一.var 声明与变量提升机制

    JavaScript中使用var定义一个变量,无论是定义在全局作用域函数函数的局部作用域中,都会被提升到其作用域的顶部,这也是JavaScript定义变量的一个令人困惑的地方。由于es5没有像其它类C语言一样的块级作用域,因此es6增加了let定义变量,用来创建块级作用域。

    我们来看一个VAR定义变量的示例:

    function   setName(){
        if(condITion){
           var name = 'loho';
           console.LOG(name);
        }else{
           console.log(name);
        }
    }
    
    var student = 'eveningwater';
    setName();
    

    以上代码可以理解成如下:

    var student;
    function setName(){
       var name;
       if(condition){
           name = 'loho';
           console.log(name);//loho
       }else{
          console.log(name);//undefined
       }
    }
    student = 'eveningwater';
    setName();
    

    二.块级声明

    块级声明意在指定一个块级作用域,使得块级作用域中所定义的变量无法再全局被访问到,块级作用域也被称为词法作用域。

    块级作用域存在于两个地方:

    1. 函数内部。
    2. 指定代码块中。(即"{"和"}"之间的区域)

    1.let 声明

    let声明同var声明用法一致,唯一的区别在于,let声明将变量限制在一个块内,这样就形成了一个块级作用域,因此也就不会存在变量的提升了。

    例如前面的示例,我们可以写成如下:

    let stundent = 'eveningwater';
    function setName(){
        if(condition){
            let name = 'loho';
           console.log(name);//loho
        }else{
           //如果条件为false执行到这里
           console.log(name);//不返回值
        }
    }
    setName();
    
    

    2.禁止重声明

    在使用let定义变量之前如果已经声明了相同的变量,就会报错。因此不能重复声明变量。如以下示例:

    var name = 'eveningwater';
    //报错,重复声明
    let name = 'loho';
    

    当然这两个变量必须是在同一个作用域中,如果是不同作用域中,则不会报错。但有可能会遮蔽第一次声明的变量。如以下示例:

    var name = 'eveningwater';
    if(condition){
       //不会报错
       let name = 'loho';
    }
    

    3.const声明

    使用const标识符所声明的变量必须要初始化,因此这个声明的就是一个常量。如下例:

    const name='eveningwater';//正确
    const name;//错误,未初始化
    

    const声明同let声明一样,也是创建了一个块级作用域,在这个块级作用域之外是无法访问到所声明的变量的。换句话说,就是const所声明的变量不会有变量提升机制。如下例:

    if(condition){
            const name = 'eveningwater';
            console.log(name);//'eveningwater'
    }
    //错误
    console.log(name);
    

    同样的const也不能重复声明,如下例:

    var name = 'eveningwater';
    //错误,不能重复声明
    const name = 'loho';
    

    但也可以在不同作用域中重复声明,如下例:

    var name = 'eveningwater';
    if(condition){
        const name = 'loho';
       console.log(name);//loho,屏蔽全局定义的变量
    }
    

    尽管const声明与let声明有太多相似的地方,但const声明也有一处与let声明不同,那就是const声明的变量不能被赋值,无论是在非严格模式下还是在严格模式下,都不能对const声明的变量进行赋值。如下例:

    const name = 'eveningwater';
    //错误
    name = 'loho';
    

    不过,如果定义的是一个对象,可以对对象的值进行修改,如下例:

    const student = {
        name:'eveningwater'
    }
    student.name = 'loho';//没问题
    //错误,相当于赋值修改对象
    student = {
       name:'loho'
    }
    

    4.临时死区

    前面提到letconst声明的变量都不会提升到作用域的顶部,因此在使用这两个标识符声明之前访问会报错,即使是typeof操作符也会触发引用错误。如下例:

    console.log(tyPEof name);//报错
    const name = 'eveningwater';
    

    由于第一行代码就报错了,因此后续的声明变量语句不会执行,此时就出现了JavaScript社区所谓的"临时死区"(temporal dead zone).虽然这里示例是const声明,但let声明也是一样的。

    但如果在constlet声明的变量的作用域之外使用typeof操作符监测却不会报错,只不过会返回undefined。如下例:

    console.log(typeof name);//undefined
    if(condition){
       let name = 'eveningwater';
    }
    

    5.循环中的块级作用域绑定

    我们在使用var声明变量的时候,总会遇到这样的情况,如下:

    for(var i = 0;i < 100;i++){
        //执行某些操作
    }
    //这里也能访问到变量i
    console.log(i);//100
    

    我们可以使用let声明将变量i限制在循环中,此时再在循环作用域之外访问变量i就会报错了,因为let声明已经为循环创建了一个块级作用域。如下:

    for(let i = 0;i < 100;i++){
        //执行某些操作
    }
    //报错
    console.log(i);
    

    6.循环中的创建函数

    在使用var声明变量的循环中,创建一个函数非常的困难,如下例:

    var func = [];
    for(var i = 0;i < 5;i++){
        func.push(function(){
            console.log(i);
        })
    }
    func.foreach(function(func){
        func();
    });
    

    你可能预期想的是打印从0到5之间,即0,1,2,3,4的数字,但实际上答案并不是如此。由于函数有自己的作用域,因此在向数组中添加函数的时候,实际上循环已经运行完成,因此每次打印变量i的值都相当于是在全局中访问变量i的值,即i = 5这个值,因此实际上答案最终会返回5次5.

    es5中,我们可以使用函数表达式(IIFE)来解决这个问题,因为函数表达式会创建一个自己的块级作用域。如下:

    var func = [];
    for(var i = 0;i < 5;i++){
        (function(i){
           func.push(function(){
               console.log(i);
           })
        })(i)
    }
    func.forEach(function(func){
        func();//这就是你想要的答案,输出0,1,2,3,4
    })
    

    ;

    但事实上有了es6let声明,我们不必如此麻烦,只需要将var变成let声明就可以了,如下:

    var func = [];
    for(let i = 0;i < 5;i++){
        func.push(function(){
            console.log(i);
        })
    }
    func.forEach(function(func){
        func();//输出0,1,2,3,4
    })
    

    但是这里不能使用const声明,因为前面提到过,const声明并初始化了一个常量之后是不能被修改的,只能在对象中被修改值。如以下示例就会报错:

    //在执行循环i++条件的时候就会报错
    for(const i = 0;i < len;i++){
        console.log(i);
    }
    

    因为i++这个语句就是在尝试修改常量i的值,因此不能将const声明用在for循环中,但可以将const声明用在for-in或者for-of循环中。如下:

    var func = [];
    var obj = {
        name:'eveningwater',
        age:22
    }
    for(let key in obj){
      func.push(function(){
          console.log(key)
      })
    }
    func.forEach(function(func){
        func();//name,age
    });
    //以下也没问题
    var func = [];
    var obj = {
        name:'eveningwater',
        age:22
    }
    for(const key in obj){
        func.push(function(){
           console.log(key)
        })
    }
    func.forEach(function(func){
      func();//name,age
    });
    

    这里并没有修改key的值,因此使用constlet声明都可以,同理for-of循环也是一样的道理。for-of循环是es6的新增的循坏。。

    7.全局作用域绑定

    let,const声明与var声明还有一个区别就是三者在全局作用域中的行为。当使用var声明一个变量时,会在全局作用域(通常情况下是浏览器window对象)中创建一个全局属性,这也就意味着可能会覆盖window对象中已经存在的一个全局变量。如下例:

    console.log(window.Array);//应该返回创建数组的构造函数,即f Array(){}
    var Array = '这是数组';
    console.log(window.Array);//返回'这是数组';
    

    从上例,我们可以知道即使全局作用域中已经定义了Array变量或者已经存在了Array属性,但我们之后定义的Array变量则会覆盖之前已经定义好的或者已经存在的Array变量(属性)。

    但是es6letconst声明则不会出现这种情况,letconst声明会创建一个新的绑定,也就是说不会成为window对象的属性。换句话说,就是所声明的变量不会覆盖全局变量,而只会遮蔽它。如下例:

    let Array = '这是数组';
    console.log(Array);//'这是数组‘;
    console.log(window.Array);//应该返回创建数组的构造函数,即f Array(){}
    

    这也就是说window.Array !== Array这个等式返回布尔值true

    8.块级绑定的最佳实践

    在使用es6块级声明变量中,最佳实践是如果确定后续不会改变这个变量的值,用const声明,如果确定要改变这个变量的值,则用let声明。因为预料外的变量值的改变时很多bug出现的头。如下示例:

    function  eveningWater(){};
    eveningWater.PRototype.name = 'eveningwater';
    let ew = new eveningWater();
    //定义不能被修改的变量,也就是用于判断实例类型的属性
    const _constructor = ew.constructor;
    //可以改变自定义的名字属性
    let  name = ew.name;
    if(_constructor ===  eveningWater || _constuctor === Object){
      console.log(_constructor);
    }else{
       name = 'loho';
       console.log(name)
    }
    

    鄙人创建了一个QQ群,供大家学习交流,希望和大家合作愉快,互相帮助,交流学习,以下为群二维码:

    es6块级作用域

    脚本宝典总结

    以上是脚本宝典为你收集整理的es6块级作用域全部内容,希望文章能够帮你解决es6块级作用域所遇到的问题。

    如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

    本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
    如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。