zoukankan      html  css  js  c++  java
  • ES6-let、const和块级作用域

      1.介绍 

      总的来说,ES6是在ES2015的基础上改变了一些书写方式,开放了更多API,这样做的目的最终还是为了贴合实际开发的需要。如果说一门编程语言的诞生是天才的构思和实现,那它的发展无疑就是不断填坑的历史。ES6正是为了填一些坑。

      我对ES6语法的学习,主要在浏览器端,参考阮一峰大神的ES6入门教程,添加了一些个人理解的注释和遇到的小问题,欢迎批评指正,共同进步。

       浏览器端引用的依赖文件和本文案例  可在 https://github.com/chanceLe/ES6-Basic-Syntax 找到。

           2.基础部分

           2.1let和const关键字

            let命令声明变量,类似var,但所声明的变量只在let代码块有效。  

               {
                    let a = 1;
                    var b = 2;
                }
                console.log(b);
                console.log(a);  //报错,找不到。
                //for循环的计数器,就很适合用let
                for(let i=0;i<5;i++){
                    console.log(i);
                }
                //上面的代码的计数器i,只在for循环体内有效。

        {}在ES6中用来形成块级作用域,后边会说到。  

          var a = [];
              for(var i=0;i<10;i++){
                  a[i] = function(){
                       console.log(i);
                    }
                }
            
          a[6]();  //10  到九循环完,又加了1,换成let会得到期望结果。

      var声明的变量全局范围内都有效。所以每次循环,新的i值都会覆盖旧值
      let声明的仅在块级作用域内有效,最后输出6

       let不会发生变量提升的现象,所以一定要在定义后使用,否则报错。

      暂时性死区:只要块级作用域内存在let命令,它所声明的变量就绑定这个区域,不再受外部影响。

           var tmp = 123;
                if(true){
                    tmp = "abc";
                    console.log(tmp,"11");  //"abc"
                    let tmp;    
                }
                console.log(tmp,"22");  //"123"

      书上说,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭
      作用域,凡是声明之前使用的,都会报错。


      实际情况,let不会报错,const会报错(这可能跟浏览器端转码有关,版本为5)。

      有些死区比较隐蔽,不太容易发现:

           function bar(x=y,y=2){
                    return [x,y];
                }
                console.log(bar());   //书上说这里由于y没有定义,应该报错,实际是 [undefined,2]
                
                
                function bar2(x=2,y=x){
                    return [x,y];
                }
                console.log(bar2());   //[2,2]正常

      不允许重复声明,let不允许在相同作用域内,重复声明同一个变量:

              function fun(){
                    let a = 10;
             //     var a = 5;   //报错。
             //     let a = 1;   //报错
                }  

      不能在函数内部重新声明参数:

          function fun1(arg){
          //    let arg;   //报错
                    {
                        let arg;  //不报错
                    }
                }

      const命令
       声明一个只读的常量。一旦声明,常量的值就不能改变。
       同时也说明,一旦声明,就要立即初始化,否则也报错。

           const PI = 3.1415;
                console.log(PI);   
    //          PI = 3;  //报错 只读的
    //          const circle;   //报错,一旦声明,必须立即初始化。
                   /*
                    * const的作用域与let命令相同:只在声明所在的块级作用域有效。
                    */
                 if(true){
                     const m = 5;
                 }
    //           console.log(m); //未定义
        

      与let相似:
       const命令声明的常量也不提升,同样存在暂时性死区,只能在声明的位置后使用。
       也不可以重复声明。

       对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只
       是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量,
       必须非常小心。

           const foo = {};
                foo.prop = 123;
                console.log(foo.prop);
                //上面的常量foo存储的是对象的地址,这个地址不可变,但依然可以添加属性。
                const a = [];
                a.push("hello");
                a.length = 0;
    //          a = ["Dave"]   //报错,赋值就是更换地址,不行的。
                //如果真的想把对象冻结,应该使用Object.freeze方法。
                const foo2 = Object.freeze({});
                //常规模式下,下面一行不起作用
                //严格模式下,会报错
                foo.prop = 123;  

      ES5只有两种声明变量的方法:var和function。  
      ES6有6种:var function let const class import


       全局对象属性:
       全局对象是最顶层的对象,在浏览器环境下指的是window对象,在node指的是
       global对象。ES5中,全局对象的属性和全局变量是等价的。
       未声明的全局变量,自动成为全局对象window的属性,这被认为是js的最大败笔。
       ES6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,
       依旧是全局对象的属性,另一方面规定,let,const,class命令声明的全局变量不属于全局对象的
       属性。也就是说,从ES6开始,全局变量将逐渐与全局对象的属性脱钩。

      2.2块级作用域:

      ES5只有全局作用域和函数作用域,没有块级作用域,有很多不合理场景:
      1.内层变量可能会覆盖外层变量。

           var tmp = new Date();
                function f(){
                    console.log(tmp);
                    if(false){
                        var tmp = "hello world!";
                    }
                }
                f();  //undefined 声明提升,后边的定义把前边的覆盖掉了。

      2.用来计数的循环变量泄漏为全局变量。典型的var定义的循环,上边有代码体现。    

      let实际上为js新增了块级作用域。下面的代码两个代码块都声明了n,运行输出5,表示外层代码不受内层代码块的影响。如果是var定义,就会收到影响,输出10;

           function f1(){
                    let n = 5;
                    if(true){
                        let n = 10;
                    }
                    console.log(n);
                }
                f1();
          //es6允许块级作用域的任意嵌套:
                {{{{{ let insane = "hello world" }}}}}
                //上面代码使用了五层块级作用域,外部不能访问内部的变量。
                //块级作用域实际上使得广泛应用的立即执行匿名函数(IIFE)不再需要了。
                //IIFE写法
                (function(){
                    
                })()
                //块级作用域写法
                {
                    
                }
                /*
           * 块级作用域与函数声明:
                 * ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块作用域中声明,
                 *   情况一:
                 *   if(true){
                 *     function(){}
                 * }
                 *   情况二:
                 *   try{
                 *       function(){}
                 * }

      这两种情况,根据ES5的规定都是非法的。但是浏览器没有遵守这个规定,可以运行,在严格
      模式下,还是会报错。


       ES6引入了块级作用域,明确允许在块级作用域中声明函数。
       块级作用域中,函数声明语句的行为类似于let,在块级作用域之外,不可以引用。
       因为块级作用域对作用域之外没有影响,这个差异会对老代码产生很大影响,为了减轻这个不兼容
       问题,ES6在附录B中规定,浏览器可以不遵守上面的规定,有自己的行为方式。


       1.允许在块级作用域内声明函数。
       2.函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
       3.同时,函数声明还会提升到所在块级作用域的头部。
       上面的3个规则只对ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域当做let处理。

       考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式。
       ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,没有使用大括号,会报错。

  • 相关阅读:
    【MongoDb入门】15分钟让你敢说自己会用MongoDB了
    【干货】基于Owin WebApi 使用OAuth2进行客户端授权服务
    【学习】在Windows10平台使用Docker ToolBox安装docker(一)
    快速搭建WebAPI(Odata+Code-First)附Odata条件查询表~
    使用QuertZ组件来搞项目工作流(一)
    AspNetCore 使用NLog日志,NLog是基于.NET平台开的类库!(又一神器)
    AspNetCore 基于流下载文件与示例代码
    再见了Server对象,拥抱IHostingEnvironment服务对象(.net core)
    AspNetCore 文件上传(模型绑定、Ajax) 两种方式 get到了吗?
    AspNetCore 目前不支持SMTP协议(基于开源组件开发邮件发送,它们分别是MailKit 和 FluentEmail )
  • 原文地址:https://www.cnblogs.com/chengyunshen/p/7191571.html
Copyright © 2011-2022 走看看