阮一峰es6第二章读书笔记-let和const命令

1. let命令

基本用法

  • 作用:用来声明变量

  • 与var区别:let声明的变量的有效范围是let命令所在的代码块,而var声明的变量是全局范围

  • 例子 :

    {
      let a = 10;
      var b = 1;
    }
    
    a // ReferenceError: a is not defined.
    b // 1
    
  • 一般可以使用在for循环中,如for(let i = 0;i<10;i++)

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

  • 例子

    for (let i = 0; i < 3; i++) {
      let i = 'abc';
      console.log(i);
    }
    // abc
    // abc
    // abc
    

不存在变量提升

  • let命令声明的变量不存在变量提升,而var命令声明的变量则会有变量提升,什么是变量提升呢,就是变量在声明之前就可以使用,但是值为undefined。

  • 例子

    // var 的情况
    console.log(foo); // 输出undefined
    var foo = 2;
    
    // let 的情况
    console.log(bar); // 报错ReferenceError
    let bar = 2;
    

暂时性死区

  • 意思就是一变量在某个区域内用let声明,那么在该区域内,任何在let声明该变量之前调用这个变量都会报ReferenceError错误

  • 例子

    if (true) {
      // TDZ开始
      tmp = 'abc'; // ReferenceError
      console.log(tmp); // ReferenceError
    
      let tmp; // TDZ结束
      console.log(tmp); // undefined
    
      tmp = 123;
      console.log(tmp); // 123
    }
    
    • 比较难看到错误的例子
    function bar(x = y, y = 2) {
      return [x, y];
    }
    
    bar(); // 报错
    

    之所以报错,是因为x=y执行的时候,y还没有声明,所以把报错

  • 暂时性死区的本质:只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,必须等到明确声明变量的一行代码出现,才可以获取和使用该变量

不允许重复声明

  • let不允许在相同的作用域内,重复声明一个变量,因此不能在函数内部重新声明函数的参数,除非你在函数内部的再加一个子作用域,就可以在自作用域内声明与参数同名的变量。

块级作用域

为什么需要块级作用域

  • 只有全局作用域和函数作用域会造成:①内层变量可能会覆盖外层变量 ②用来计数的循环变量泄露为全局变量

    • 对于第一种 例子:
    var tmp = new Date();
    
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }
    
    f(); // undefined
    

    内层if语句的tmp覆盖了外层时间的tmp

    • 对于第二种 例子
     var s = 'hello';
    
    for (var i = 0; i < s.length; i++) {
      console.log(s[i]);
    }
    
    console.log(i); // 5
    

    i变成了全局变量

ES6的块级作用域

  • let创造了块级作用域,使得外层作用域无法读取内层作用域的变量,并且内层作用域可以定义外层作用域的同名变量。

    • 例子
     function f1() {
      let n = 5;
      if (true) {
        let n = 10;
      }
      console.log(n); // 5
    }
    

    这个例子,如果都是用var的话,n就会编程全局变量,结果为10

块级作用域与函数声明

  • ES6规定,函数可以在块级作用域中声明,并且块级作用域外不能调用该函数。
  • 浏览器实现
    • 允许在块级作用域内声明函数
    • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
    • 同时,函数声明还会提升到所在的块级作用域的头部
  • 其他地方则把块级作用域的函数声明当做let处理
  • 最佳实践:由于韩静导致行为差异太大,所以应该避免在块级作用域内声明函数,如果确实需要,那么携程函数表达式形式
  • 其他注意事项:ES6 的块级作用域允许声明函数的规则,只在大括号的情况下成立,所以if后面一定要加括号,即使后面只有一句话。

const 命令

  • 作用:声明一个只读的常量。一旦声明,常量的值就不能改变

  • const声明的变量一旦声明就必须马上初始化,不能以后赋值,否则会报错

    const foo;
    // SyntaxError: Missing initializer in const declaration
    
  • const与let类似只在声明所在的块级作用域内有效,同样存在暂时性死去,只能在声明的位置之后使用,同样不能重复声明。

本质

  • const实际上保证不是说变量的值不能改动,而是变量指向的那个内存地址所保存的数据不得改动,那么对于简单的数据类型(数值,字符串,布尔值)来说,它们的值就保存在变量所指的地址,因此等同于变量,但是对于函数或数组来说,变量指向的内存地址,保存的知识一个指向实际数据的指针,const只能保证这个指针是固定的,(即总是执行另一个固定的地址),至于它指向的数据结构就可以变化了。例子

    const foo = {};
    
    // 为 foo 添加一个属性,可以成功
    foo.prop = 123;
    foo.prop // 123
    
    // 将 foo 指向另一个对象,就会报错
    foo = {}; // TypeError: "foo" is read-only
    

    简单来说,就是const只能保证变量是指向这个对象或这个数组,这点是不能变化的,但是对象或数据里面的内容则不能管理到。

Object.freeze方法

  • 作用:将对象冻结,使之不能添加新的属性

  • 例子

     const foo = Object.freeze({});
    
    // 常规模式时,下面一行不起作用;
    // 严格模式时,该行会报错
    foo.prop = 123;
    

ES6声明变量的六种方法

六种方法分别是:var function let const import class

顶层对象的属性

  • 顶层对象,浏览器中指window对象,node中指global对象,ES5中顶层对象的属性与全局属性是等价的 例子:

    window.a = 1;
    a // 1
    
    a = 2;
    window.a // 2
    
  • 顶层对象与全局变量等价造成的问题:①没法在编译时就爆出变量未声明的错误②程序员很容易不知不觉就创建了全局变量③顶层对象的属性到处可以读写,不利于模块化编程。

  • ES6将全局变量逐步与顶层对象的属性脱钩:表现在varfunction命令声明的全局变量依旧是顶层对象属性,但let命令,const命令、class命令声明的全局变量不属于顶层属性。例子

    var a = 1;
    // 如果在 Node 的 REPL 环境,可以写成 global.a
    // 或者采用通用方法,写成 this.a
    window.a // 1
    
    let b = 1;
    window.b // undefined
    

global对象

global是ES5的顶层对象,但在各种实现里面不统一。

  • 浏览器里面,顶层对象是window,但Node和Web Worker没有window。

  • 浏览器和Web Worker里面,self也指向顶层独享,但是Node没有self

  • node里面,顶层对象是golbal,但其他环境不支持
    而为了一段代码能在各种环境都能取到顶层对象,一般用this变量,但是有它的局限性

  • 全局环境中,this会返回顶层对象,但是,node模块和es6模块中,thils返回的是当前模块

  • 函数里面的this,如果不是作为对象的方法运行,而是单纯作为对象运行,this会指向顶层对象,但是,严格模式下,这时this会返回undefined。

  • 不管是严格模式,还是普通模式,new Function(‘return’)()总是会返回全局对象。
    因此很闹找到一种方法在所有情况下,都取到顶层对象,下面是两种勉强可以使用的方法。

    // 方法一
    (typeof window !== 'undefined'
       ? window
       : (typeof process === 'object' &&
          typeof require === 'function' &&
          typeof global === 'object')
         ? global
         : this);
    
    // 方法二
    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');
    };
    

参考资料

阮一峰的es6教程

你可能感兴趣的:(阮一峰es6第二章读书笔记-let和const命令)