• let/const及块级作用域


    本系列是在平时阅读、学习、实际项目中有关于es6中的新特性、用发的简单总结,目的是记录以备日后温习;本系列预计包含let/const、箭头函数、解构、常用新增方法、Symbol、Set&Map、Proxy、reflect、Class、Module、Iterator、Promise、Generator、async/await

    let/const为我们带来了什么?

    let

    1. 约束变量提升
      (function foo() {
              console.log(a);
              let a = 1;
          })();
      

      // Uncaught ReferenceError: a is not defined

      总结下来就是一句: 在变量使用之前,必须先要声明,变量声明永远在使用之前。

    2. 带来了块级作用域
      复制代码
      // es5
          (function(){
              if(false) {
                  var temp = 1;
              }
              console.log(temp); // undefined
          })();
      
      </span><span style="color: #008000;">//</span><span style="color: #008000;"> es6</span>
      (<span style="color: #0000ff;">function</span><span style="color: #000000;">(){
          </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">false</span><span style="color: #000000;">) {
              let temp </span>= 1<span style="color: #000000;">;
          }
          console.log(temp); </span><span style="color: #008000;">//</span><span style="color: #008000;"> Uncaught ReferenceError: temp is not defined</span>
      })();</pre>
      
      复制代码

      从代码中我们可以很清晰看到es6的let的块级作用域,那么块级作用域有什么应用呢?举个例子:

      复制代码
      var fnArr = [];
          for(var i = 0; i < 5; i++) {
              fnArr.push(function() {
                  console.log(i);
              });
          }
          fnArr[0](); // 5
          fnArr[1](); // 5
          fnArr[2](); // 5
          fnArr[3](); // 5
          fnArr[4](); // 5
          console.log(i); // 5
      复制代码

      如果没有仔细分析,执行的结果是不是有些出乎意料呢? 是的,我们本意在for循环内部使用的变量i被泄露成了全局变量,而且在for循环的每一次循环,变量i并没有被重新声明,实际上数组fnArr中保存的每一个函数中引用的都是同一个变量i,所以才导致了现在的结果,那怎么让代码按照我们最初的想法运行呢?来看es5中常用的解法

      复制代码
      var fnArr1 = [];
          for(var i = 0; i < 5; i++) {
              (function(j) {
                  fnArr1.push(function() {
                      console.log(j);
                  });
              })(i)
          }
          fnArr1[0](); // 0
          fnArr1[1](); // 1
          fnArr1[2](); // 2
          fnArr1[3](); // 3
          fnArr1[4](); // 4
          console.log(i); // 5
      复制代码

      看起来是解决了,这里实际上用到了闭包的方法,fnArr1中每一项函数引用的j都是当前循环时i的一个副本,这样就解决了前面的问题,但是还有一个问题: 变量i仍然隐式得泄露到了全局

      复制代码
      var fnArr1 = [];
          for(let i = 0; i < 5; i++) {
              fnArr1.push(function() {
                  console.log(i);
              });
          }
          fnArr1[0](); // 0
          fnArr1[1](); // 1
          fnArr1[2](); // 2
          fnArr1[3](); // 3
          fnArr1[4](); // 4
          console.log(i); // Uncaught ReferenceError: i is not defined
      复制代码

      OK,问题解决了,仅仅是将’var‘替换成了let,这就是let带来的便利。

    3. 产生暂时性死区&禁止重复声明
      复制代码
      // 什么是禁止重复声明呢? 先不给书面解释,来看一个es5中经常的写法
      (function() {
              var temp = 1;
              var temp = 2;
              var temp = function() {
                  return 1;
              };
          })();
      复制代码

      上面这段代码执行没有任何问题,最终temp被赋值为一个函数

      复制代码
      (function() {
              let temp = 1;
              var temp = 2; // Uncaught SyntaxError: Identifier 'temp' has already been declared
              var temp = function() {
                  return 1;
              };
          })();
      复制代码

      在第三行时就抛出了一个错误:temp已经被声明,是的,let声明过的变量,是不允许再次被声明的,再给几个例子巩固一下:

      复制代码
      (function() {
              var temp = 2;
              var temp = function() {
                  return 1;
              };
              let temp = 1; // Uncaught SyntaxError: Identifier 'temp' has already been declared
          })();
      
      (</span><span style="color: #0000ff;">function</span><span style="color: #000000;">() {
          </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">true</span><span style="color: #000000;">) {
              let temp </span>= 1<span style="color: #000000;">;
              </span><span style="color: #0000ff;">var</span> temp = 2; <span style="color: #008000;">//</span><span style="color: #008000;"> Uncaught SyntaxError: Identifier 'temp' has already been declared</span>
      

      }
      })();

      (function() {
      if(true) {
      let temp
      = 1;
      function temp() { // Uncaught SyntaxError: Identifier 'temp' has already been declared
      return 1;
      }
      }
      })();

      复制代码

      看出来了吗?只要是在let声明所在的作用域,就不允许再次声明同名变量(包括函数声明)

      var foo = 1;
          if(true) {
              foo = 2; // Uncaught ReferenceError: foo is not defined
              let foo;
          }

      看到let的’霸道‘了吧?只要在let所在的作用域,同名的变量就会被let占有,不允许重复声明,同时也要遵守let的规则

    4. 全局变量不再作为window对象的属性
      var foo = 1;
          (function() {
              bar = 2;
          })();
          window.foo; // 1
          window.bar; // 2

      是的,es5中,全局变量(包括意外泄露的)都将自动被添加为window对象的属性

      let foo = 1;
      

      window.foo; // undefined

      一切尽在不言中。。。

    const

    1. let所拥有的特性,const都有,同时const还有一条:const声明的变量必须进行初始化,并且不能再被重新赋值
      const temp = 1;
          temp = 2; // Uncaught TypeError: Assignment to constant variable.

      注意是不能被重新赋值,这样是比较准确的,其实const声明的变量是可以被修改的,当const声明的变量被初始化为复杂数据类型时,const声明的变量就是可变的,至于为什么,自己理解喽(变量标识符中保存的只是复杂数据类型内存地址而已。。。)

      const temp = {};
          temp.foo = 'aa'; // 这里没问题
          temp = {foo: 'aa'}; // 这里就会抛出异常

    for循环中的变量声明

    前面在记录let块级作用域的时候,我们使用了一个for循环的例子,这里我们不妨试着解析一下for循环的执行过程

    复制代码
    var fnArr = [];
        for(var i = 0; i < 3; i++) {
            fnArr.push(function() {
                console.log(i);
            });
        }
    
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 伪代码</span>
    <span style="color: #0000ff;">var</span><span style="color: #000000;"> fnArr;
    fnArr </span>=<span style="color: #000000;"> [];
    {
        </span><span style="color: #0000ff;">var</span><span style="color: #000000;"> i;
        i </span>= 0<span style="color: #000000;">;
    
        </span><span style="color: #0000ff;">if</span>(i &lt; 3<span style="color: #000000;">) {
            fnArr.push(</span><span style="color: #0000ff;">function</span><span style="color: #000000;">() {
                console.log(i);
            })
        }
        i</span>++<span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span>(i &lt; 3<span style="color: #000000;">) {
            fnArr.push(</span><span style="color: #0000ff;">function</span><span style="color: #000000;">() {
                console.log(i);
            });
        }
        i</span>++<span style="color: #000000;">;
        ...
    }</span></pre>
    
    复制代码

    这里可惜清晰得看到所有的i都是一个i。。。那使用了let以后呢?

    复制代码
    var fnArr = [];
        for(let i = 0; i < 3; i++) {
            fnArr.push(function() {
                console.log(i);
            });
        }
    
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 伪代码</span>
    <span style="color: #0000ff;">var</span><span style="color: #000000;"> fnArr;
    fnArr </span>=<span style="color: #000000;"> [];
    {
        let i;
        i </span>= 0<span style="color: #000000;">;
    
        </span><span style="color: #0000ff;">if</span>(i &lt; 3<span style="color: #000000;">) {
            let i </span>=<span style="color: #000000;"> i;
            fnArr.push(</span><span style="color: #0000ff;">function</span><span style="color: #000000;">() {
                console.log(i);
            })
        }
        i</span>++<span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span>(i &lt; 3<span style="color: #000000;">) {
            let i </span>=<span style="color: #000000;"> i;
            fnArr.push(</span><span style="color: #0000ff;">function</span><span style="color: #000000;">() {
                console.log(i);
            });
        }
        i</span>++<span style="color: #000000;">;
        ...
    }</span></pre>
    
    复制代码

    是不是看出点名堂?其实我们完全可以这样理解,在每一次循环中都重新声明了i,并且被赋值为外层i的当前值。(注意啊,这里只是伪代码,便于理解,实际中let i = i是会抛出异常的)

    转载来源:https://www.cnblogs.com/innooo/p/10438947.html

  • 相关阅读:
    java.lang.NoClassDefFoundError: org/springframework/dao/support/DaoSupport
    project configuration is not up-to-date with pom.xml
    消息列队5
    消息列队4
    消息列队3
    聊聊常见的数据库架构设计方案?
    消息队列2
    消息队列1
    搜索引擎5
    搜索引擎4
  • 原文地址:https://www.cnblogs.com/itzlg/p/11300764.html
Copyright © 2020-2023  润新知