zoukankan      html  css  js  c++  java
  • 总结下var、let 和 const 的区别

    一、var变量

    复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>var</title>
        <script>
            window.onload = function(){
                var aLi = document.getElementsByTagName('li');
                for (var i=0;i<aLi.length;i++){  /*将var改为let*/
                  aLi[i].onclick = function(){
                  alert(i);    /*单击任何标签都输出4*/
            }
        }
    }
        </script>
    </head>
    <body>
        <ul>
            <li>0</li>
            <li>1</li>
            <li>2</li>
            <li>3</li>
    </ul>
    </body>
    </html>
    复制代码

    二、let变量

    ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景,在ES6之前,大部分人会选择使用闭包来解决这个问题,今天我们使用ES6提供的let来解决这个问题。

    代码大同小异,只需将上例子代码for循环中的var改为let,即可实现的效果是点击不同的<li>标签,alert出其对应的索引值。

    复制代码
    window.onload = function(){
        var aLi = document.getElementsByTagName('li');
        for (let i=0;i<aLi.length;i++){
            aLi[i].onclick = function(){
                alert(i);
            }
        };     
    }
    复制代码

    let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let为其声明的变量隐式地了所在的块作用域。  

    就是 for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。

    var和let的区别

    1.函数作用域 vs 块级作用域

    var 和 let 第一点不同就是 let 是块作用域,即其在整个大括号 {} 之内可见。如果使用 let 来重写上面的 for 循环的话,会报错

    var:只有全局作用域和函数作用域概念,没有块级作用域的概念。但是会把{}内也假称为块作用域。

    let:只有块级作用域的概念 ,由 { } 包括起来,if语句和for语句里面的{ }也属于块级作用域。

    /*for循环,for循环里面是父级作用域,循环体内是另一个*/
    for( let i = 0 ; i < 3 ; i++ ){
        let i = 'abc'    //用var替代let会报错提示已经定义,若没有任何关键字则每次赋值给i,最后只会输出一次abc
        console.log(i)    // 输出3次abc  
      }

          

          

       

    复制代码
    function varTest() {
      var x = 31;
      if (true) {
        var x = 71;  // same variable!
        console.log(x);  // 71
      }
      console.log(x);  // 71
    }
    
    function letTest() {
      let x = 31;
      if (true) {
        let x = 71;  // different variable
        console.log(x);  // 71
      }
      console.log(x);  // 31
    }
    复制代码

    2.变量提升 vs 暂时性死区

       let 和 var 的第二点不同是,在变量声明之前就访问变量的话,会直接提示 ReferenceError,而不像 var 那样使用默认值 undefined:

        var 存在变量提升,而 let,const(后面会提及)声明的变量却不存在变量提升,所以用 let 定义的变量一定要在声明后再使用,否则会报错。

    复制代码
    <script>
            /*1.var变量*/
            console.log(a);  //undefined
            var a=1;
         b=10; 
            console.log(b);  //10
            var b;
            
            /*2.let变量*/
            console.log(c); // Uncaught ReferenceError: c is not defined
            let c=2;
            console.log(d); // Uncaught ReferenceError: d is not defined
            let d;
    </script>
    
    <script>
    var x = 5; // 初始化 x
    elem = document.getElementById("demo"); // 查找元素 
    elem.innerHTML = "x 为:" + x + ",y 为:" + y;           // 显示 x 和 y
    var y = 7; // 初始化 y
    </script>
    
    结果输出: x 为:5,y 为:undefined
    
    y 输出了 undefined,这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量。
    <script>
    a=5;
    show();
    var a;
    function show(){};
    预解析:
    
    function show(){};
    var a;
    a=5;
    show();  //需要注意都是函数声明提升直接把整个函数提到执行环境的最顶端。
    </script>
    复制代码

     可以看出,虽然代码中console调用a在前,声明a在后,但是由于在js中,函数及变量的声明都将被提升到函数的最顶部,也就是说(var声明的)变量可以先使用再声明。

    ES6明确规定,如果区块中存在let命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。所以在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

    let a = 'outside';
    if(true) {
       console.log(a);//Uncaught ReferenceError: a is not defined
        let a = "inside";
    }

    当前作用域顶部到该变量声明位置中间的部分,都是该let变量的死区,在死区中,禁止访问该变量。由此,我们给出结论,let声明的变量存在变量提升, 但是由于死区我们无法在声明前访问这个变量

    “暂时性死区”也意味着typeof不再是一个百分之百安全的操作,因为会使typeof报错。 

    {
    typeof name;//ReferenceError
    let name;
    }

    只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块中,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死亡”。

    3.let不允许重复声明变量

    可以看出,var:变量可以多次声明,而let不允许在相同作用域内,重复声明同一个变量。

    复制代码
    <script>
        if (true) {
          let a;
          let a; // Uncaught SyntaxError: Identifier 'a' has already been declared
        }
    
        if(true){
          var d;
          var d;    //不会报错
        }
    
        if (true) {
          var c;
          let c; // Uncaught SyntaxError: Identifier 'c' has already been declared
        }
    
        if (true) {
          let d;
          var d; // Uncaught SyntaxError: Identifier 'd' has already been declared
        }
    </script>
    复制代码

    4.全局变量vs全局对象的属性

    ES5中全局对象的属性与全局变量基本是等价的,但是也有区别,比如通过var声明的全局变量不能使用delete从 window/global ( global是针对与node环境)上删除,不过在变量的访问上基本等价。

    ES6 中做了严格的区分,使用 var 和 function 声明的全局变量依旧作为全局对象的属性,使用 letconst 命令声明的全局变量不属于全局对象的属性。

    复制代码
    <script>
        var a = 10;
        console.log(window.a); //10
        console.log(this.a) //10
    
        let b = 20;
        console.log(window.b); // undefined
        console.log(this.b) // undefined
    </script>
    复制代码

    三、const声明的常量

    除了let以外,ES6还引入了cons,const 和 let 的作用域是一致的,不同的是 const 变量一旦被赋值,就不能再改变了,但是这并不意味着使用 const 声明的变量本身不可变,只是说它不可被再次赋值了,而且const 声明的变量必须经过初始化。

    const a = 1;
    
    a = 2; // // Uncaught TypeError: Assignment to constant variable
    const b; // Uncaught SyntaxError: Missing initializer in const declaration

    注:复合类型const变量保存的是引用。因为复合类型(如数组和对象)的常量不指向数据,而是指向数据(heap)所在的地址(stack),所以通过 const 声明的复合类型只能保证其地址引用不变,但不能保证其数据不变。所以将一个对象声明为常量必须非常小心。

    简单数据类型(数值,字符串,布尔值):值保存在变量指向的那个内存地址,因此等同于常量。

    复合类型的数据(对象和数组):变量指向的是内存地址,保存的是一个指针,const只能保存这个指针地址是固定的,至于他指向的数据结构是不是可变的,就完全不能控制了。

    复制代码
    <script>
        /*不会报错,因为names1指向的地址不变,改变的只是内部数据*/
        const names1 = [];
              names1[0] = 1;
              names1[1] = 2;
              names1[2] = 3;
              names1[3] = 10;
        console.log(names1);
    
        /*出错,因为变量names2指向的地址不能发生改变,应始终指向[]所在的地址,[1,4]与[6,7]不是同一个地址*/
        const names2=[1,4];
              names2=[6,7];   //报错
    </script>
    复制代码

     最后

    但是什么时候用 var、let 或 const 呢?我的建议是,大多数情况下都使用 const,除非你知道你的变量的值还会被改变,以上大概是总结后的内容,看来,还是多用 let 、const 吧。

  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/-ling/p/13563149.html
Copyright © 2011-2022 走看看