let、const和var的区别比较详解

前言

首先,须要知道let、const、var都是声明变量的命令。let和const是es6中新增的。javascript

顺便说下,es6中共6种声明命令,还有function、import、class。而在es5或之前,只有var和function两种。html

而后,须要知道es6(ECMAScript6)是2015才肯定的,es是标准,而js(javascript)是es的一个实现。java

最后,既然es6出现不是好久,必然不会所有浏览器都支持,目前有babel这些包能够对es6转为es5。node

好,下面开始讲这三者区别。es6

let和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中用来造成块级做用域,后边会说到。  babel

      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不会发生变量提高的现象,因此必定要在定义后使用,不然报错。es5

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

       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;

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

  块级做用域:

  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处理。

最后说说var

首先,ECMAScript和JavaScript关系: 
      ECMAScript是一个国际经过的标准化脚本语言。JavaScript由ECMAScript和DOM、BOM三者组成。能够简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展。

1. 块做用域{ }

JS中做用域有:全局做用域、函数做用域。没有块做用域的概念。ECMAScript 6(简称ES6)中新增了块级做用域。 
块做用域由 { } 包括,if语句和for语句里面的{ }也属于块做用域。

<script type="text/javascript">  
    {  
        var a = 1;  
        console.log(a); // 1  
    }  
    console.log(a); // 1  
    // 可见,经过var定义的变量能够跨块做用域访问到。

    (function A() {  
        var b = 2;  
        console.log(b); // 2  
    })();  
    // console.log(b); // 报错,  
    // 可见,经过var定义的变量不能跨函数做用域访问到

    if(true) {  
        var c = 3;  
    }  
    console.log(c); // 3  
    for(var i = 0; i < 4; i ++) {  
        var d = 5;  
    };  
    console.log(i); // 4   (循环结束i已是4,因此此处i为4)  
    console.log(d); // 5  
    // if语句和for语句中用var定义的变量能够在外面访问到,  
    // 可见,if语句和for语句属于块做用域,不属于函数做用域。  
</script>

2. var、let、const的区别

var定义的变量,没有块的概念,能够跨块访问, 不能跨函数访问。
let定义的变量,只能在块做用域里访问,不能跨块访问,也不能跨函数访问。
const用来定义常量,使用时必须初始化(即必须赋值),只能在块做用域里访问,并且不能修改。

<script type="text/javascript">  
    // 块做用域  
    {  
        var a = 1;  
        let b = 2;  
        const c = 3;  
        // c = 4; // 报错  
        var aa;  
        let bb;  
        // const cc; // 报错  
        console.log(a); // 1  
        console.log(b); // 2  
        console.log(c); // 3  
        console.log(aa); // undefined  
        console.log(bb); // undefined  
    }  
    console.log(a); // 1  
    // console.log(b); // 报错  
    // console.log(c); // 报错

    // 函数做用域  
    (function A() {  
        var d = 5;  
        let e = 6;  
        const f = 7;  
        console.log(d); // 5  
        console.log(e); // 6  (在同一个{ }中,也属于同一个块,能够正常访问到)  
        console.log(f); // 7  (在同一个{ }中,也属于同一个块,能够正常访问到)

    })();  
    // console.log(d); // 报错  
    // console.log(e); // 报错  
    // console.log(f); // 报错  
</script>

 

   考虑到环境致使的行为差别太大,应该避免在块级做用域内声明函数。若是确实须要,也应该写成函数表达式。
   ES6的块级做用域容许声明函数的规则,只在使用大括号的状况下成立,没有使用大括号,会报错。

参考:

http://www.javashuo.com/article/p-bsiyjunf-mr.html

https://blog.csdn.net/hot_cool/article/details/78302673

相关文章
相关标签/搜索