美文网首页ES6标准入门
Z02_let 和 const 命令

Z02_let 和 const 命令

作者: Eastboat | 来源:发表于2019-12-25 01:16 被阅读0次

    let 和 const 命令

    for 循环中的特别之处,设置循环变量的那部分是一个父作用域,循环体内部是一个单独的子作用域

    for (let i = 0; i < 3; i++) {
      let i = "aaa";
      console.log(i); //打印三次都是aaa说明和上面不在同一个作用域
    }
    
    //原始es5
    const arr = [];
    for (var i = 0; i < 10; i++) {
      arr[i] = function() {
        console.log(i);
      };
    }
    arr[5](); //10
    arr[9](); //10
    
    //改写
    const arr2 = [];
    for (let i = 0; i < 10; i++) {
      arr2[i] = function() {
        console.log(i);
      };
    }
    arr2[5](); //5
    arr2[3](); //3
    

    let


    1. 不存在变量提升

    2. 暂时性死区 TZD

    只要块级作用域内存在 let 命令,他声明的变量就 binding 了这个区域,不受外部影响
    ,在 let 声明变量之前都是死区,意味着 typeof 不再是一个百分之百的操作

    var temp = 2;
    if (true) {
      temp = "222"; //报错
      console.log(typeof temp); //ReferenceError
      let temp; //此处以上的代码声明temp,都属于temp变量的暂时性死区
    }
    

    有些暂时性死区比较隐蔽

    function bar(x = y, y = 2) {
      //此时参数x的默认值等于y,但y还没有声明,属于死区
      return [x, y];
    }
    console.log(bar(1, 2)); //[1,2]
    bar(); //ReferenceError: y is not defined
    
    //如果 y的默认值是x就不会报错
    function bar2(x = 2, y = x) {
      console.log(x, y);
    }
    bar2(); // 2,2
    

    3. 不允许在相同作用域内重复声明同一个变量

    function func(args) {
      let args; //不熬在函数内部重新声明参数
    }
    

    块级作用域

    1. 为什么需要块级作用域?

    1. 内层变量可以覆盖外层变量
    2. 用来计数的循环变量泄露成全局变量
      使得广泛应用的立即执行匿名函数(IIFE)不在必要了
    (function() {
      var temp = "aaa";
    })();
    
    //块级作用域
    {
      let temp = "aaa";
    }
    

    2. 块级作用域和函数

    ES5 规定函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明

    //情况 1
    if (true) {
      function fn() {}
    }
    //情况 2
    try {
      function fn() {}
    } catch (e) {}
    

    以上两个函数声明在 ES5 中是非法的,在 es6 中应该避免在块级作用域中声明函数,如果需要可以应该写成函数表达式的形式

    es6 允许在块级作用域中声明函数,在块级作用域中,函数声明语句的行为类似于 let,在块级作用域之外不可引用

    //以下代码会报错
    
    function f() {
      console.log("外面函数f");
    }
    
    (function() {
      if (false) {
        function f() {
          console.log("里面函数f");
        }
      }
      f();
    })();
    
    //实际运行的情况如下
    
    function f() {
      console.log("外面函数f");
    }
    
    (function() {
      var f = undefined;
      if (false) {
        function f() {
          console.log("里面函数f");
        }
      }
      f();
    })();
    

    考虑到环境导致的行为差异太大,应该避免在块级作用域中声明函数,确实需要的话,应该写成函数表达式的形式,而不是函数声明语句

    {
      let a=123;
      function f(){return a}
    }
    
    //确实需要的话
    {
      let a=123;
      let f=function(return a)
    }
    
    
    

    3. do 表达式

    本质上块级作用与是一个语句,将多个操作封装在一起,没有返回值,可以使用 do

    //以下块级作用域中不返回值,此作用域之外无法得到t的值,除非t是全局变量
    {
      let t = f();
      t * t * t + 1;
    }
    

    新的提案,在块级作用域前加上 do,使他变为 do 表达式

    let x = do {
      let t = f();
      t * t * t + 1;
    }; //x 会得到整个块级作用域的返回值
    

    const 命令

    const 声明一个只读的常量,一旦声明,常量的值就不能改变

    1. 注意只声明不赋值会报错

    const MAX; //报错
    

    2. const 和 let 的作用域一样,只在声明所在的块级作用域内有效

    if (true) {
      const MAX = 555;
    }
    console.log(MAX); //报错
    

    3. const 命令也不存在变量提升,同样存在暂时性死区

    if (true) {
      console.log(MAX); //报错
      const MAX = 5555;
    }
    

    4. 和 let 一样不可以重复声明

    var a = 1;
    let b = 2;
    //下面两行报错
    const a = 3;
    const b = 4;
    

    const的本质实际是保证变量指向的那个内存的地址不得改动,对于引用数据类型来讲,变量指向内存的那个指针,const只能保证这个指针是固定的,至于他指向的数据结构是不可变的,这完全不能控制,所以将一个对象声明为一个常量要格外小心

    const foo = {};
    foo.name = "foo";
    foo = {}; //指向新的对象会报错
    
    const arr = [];
    arr.push("a");
    arr = [1, 2, 3]; //赋值为新的数组报错
    

    如果想冻结对象,应该使用 Object.freeze({})

    const obj = Object.freeze({
      name: "cc",
      age: 12
    });
    
    console.log(obj); //{ name: 'cc', age: 12 }
    obj.name = 123; //严格模式下会报错
    console.log(obj); //{ name: 'cc', age: 12 }
    //常量指向一个冻结的对象,所以给他添加新的属性是不起作用的,严格模式还会报错
    

    除了对象本身冻结,对象的属性也应该冻结,下面是将对象彻底冻结的函数

    var constantize = obj => {
      Object.freeze(obj);
      Object.keys(obj).forEach((key, i) => {
        if (typeof obj[key] === "object") {
          constantize(obj[key]);
        }
      });
    };
    

    5. ES6 中声明变量的 6 中方法

    ES5 声明变量只有 varfunction
    ES6 声明 const let import class

    顶层对象的属性

    顶层对象在浏览器环境中是指 window,在 node 中是指 global 对象

    //ES5中,顶层对象的属性和全局变量是等价的
    window.a=1 =====  var a=1
    //ES6开始全局变量将逐步与顶层对象的属性隔离
    let b=1  !====  window.b //undefined
    

    global 对象

    ES5 的顶层对象本身也是一个问题,因为他在各种实现中是不统一的

    1. 浏览器中顶层对象就是 windoqm,但是 Node 和 Web Worker 中没有 window
    2. 浏览器和 Web Workder 中,self 也指向顶层对象,但是 Node 没有 self
    3. 在 Node 中,顶层对象是 global,但是其他环境都不支持

    同一端代码为了能够在各种环境中都能获取顶层对象,目前一般是采用 this 变量,但是也有局限性

    1. 全局环境中,this 返回顶层对象,但是 ES6 模块和 node 模块中,this 返回的是当前模块 2.对于函数 this,如果函数不是以对象的方式运行,而是单纯作为函数运行,this 会指向顶层对象,但是严格模式下,this 指向 undefined
    2. 不管是严格模式还是普通模式,new Function('return this')()总是返回全局对象

    所以很难有一种方法可以适合上面所有场景,取得顶层对象,以下是两种勉强可以使用的方法

    //方法一
    var a =
      typeof window !== "undefined"
        ? window
        : typeof process === "object" &&
          typeof require === "function" &&
          typeof global === "object"
        ? global
        : this;
    
    // console.log(a);
    //方法二
    
    var getGlobal = function() {
      if (typeof self !== "undefined") {
        return self;
      }
      if (typeof window !== "undefined") {
        return window;
      }
      if (typeof global !== "undefined") {
        return global;
      }
      throw new Error("unable to locate global object");
    };
    console.log(getGlobal());
    

    相关文章

      网友评论

        本文标题:Z02_let 和 const 命令

        本文链接:https://www.haomeiwen.com/subject/coyqoctx.html