【读书笔记】《ES6 入门》学习一

221 阅读7分钟

前言


  知识的学习需要从两方面入手,一方面是天马行空的想象力,联想拓展,思维活跃,才能把握知识主干与发展趋势。我们往往过于重视这一点,从而忽略了另一方面,那就是系统化学习知识的基础点,打牢地基,训练一套成熟的学科思维方法。要做到这一点,需要花费很多时间和精力,进度较为缓慢,但这是内功,影响了我们后续发展的潜力与动力。慢就是快。   之前对于ES6的语法都是零零落落,毫无章法。故此,系统化的学习,每天进步一点点,持之以恒,收货一定是巨大的。

正文


let和const命令

  1. let命令

    • 基本用法 ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
    • for循环作用域
      var a = [];
      for (let i = 0; i < 10; i++) {
          a[i] = function () {
          console.log(i);
          };
      }
      a[6](); // 6
    

     上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的 变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮 循环的 值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮 的变量i时,就在上一轮循环的基础上进行计算。  另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

    • 不存在变量提升 var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined
    • 暂时性锁区 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
    • 不允许重复声明 let不允许在相同作用域内,重复声明同一个变量。
  2. 块级作用域
    ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。let实际上为 JavaScript 新增了块级作用域。

  3. const命令

    • 基本用法 const声明一个只读的常量。一旦声明,常量的值就不能改变。const声明的变量不得改变值,这意味 着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。对于const来说,只声明不赋值,就 会报错。 const的作用域与let命令相同:只在声明所在的块级作用域内有效。const命令声明的常量也是不提 升,同样存在暂时性死区,只能在声明的位置后面使用。const声明的常量,也与let一样不可重复声明。
    • 本质 const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
    • ES6声明变量的6种方法 ES5只有两种声明变量的方法:varunction。ES6又新增了四种,除了上面所讲的letconst,还有import命令和class命令。
  4. 顶层对象的属性(待完善)
    顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。

window.a = 1
a // 1

b = 2
window.b // 2

上面代码中,顶层对象的属性赋值与全局变量的赋值,是一回事。
5. global对象

变量的解构与赋值

  1. 数组的解构赋值

    • 基本用法 ES6 允许按照一定模式,从数组或对象中提取值,对变量进行赋值,这被称作解构(Destructuring)。 以前变量只能直接赋值:
    let a = 1;
    let b = 2;
    let c = 3;
    

    现在ES6允许这样赋值:

    let [a, b, c] = [1, 2, 3]
    

    上面代码表示,从数组中提取值,按照对应位置,对变量赋值。 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。 事情的结果有成功,也就会有失败,解构成功自然符合我们的意愿,那如果失败了的话,那需要被考虑。

    let [foo] = []
    let [bar, foo] = [1] 
    

    以上两种解构都不成功,foo的值都会等于undefined。 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构
    依然可以成功。

     let [a, b] = [1, 2, 3]
     a // 1
     b // 2
    
     let [a, [b], d] = [1, [2, 3], 4];
     a // 1
     b // 2
     d // 4
    

    上面两个例子,都属于不完全解构,但是可以成功。 如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。

      // 报错
      let [foo] = 1;
      let [foo] = false;
      let [foo] = NaN;
      let [foo] = undefined;
      let [foo] = null;
      let [foo] = {};
    
    • 默认赋值 解构赋值允许指定默认值。
     let [foo = true] = [];
     foo // true
    
     let [x, y = 'b'] = ['a']; // x='a', y='b'
     let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
    

    注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格 等于undefined,默认值才会生效。

  2. 对象的解构赋值 解构不仅仅可以用在数组上,还可以用在对象上面。

    • 基本用法
    let {foo, bar}  = {foo: 'last', bar: 'next'}
    
    foo // 'last'
    bar // 'next'
    

    对象的解构与数组有个很大的不同,数组的元素是按次序排列的,变量的取值由它的位置决定。而对象的 属性没有次序,变量必须与属性同名,才能取到正确的值。

    let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"
    
    let { baz } = { foo: 'aaa', bar: 'bbb' };
    baz // undefined
    

    上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取 值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。 如果解构失败,变量的值等于undefined。 对象的结构赋值,可以很方便的将现有对象的方法,赋值给某个变量。

    // 例一
    let { log, sin, cos } = Math;
    
    // 例二
    const { log } = console;
    log('hello') // hello
    

    上面例一将Math对象的对数,正弦,余弦方法赋值给了log,sin,cos变量。这样一来,可以直接通过变量调用Math的方法,更加便利。

  3. 字符串的解构赋值
    字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    

    类似数组的对象都有一个length属性,因此还可以对这个属性进行解构赋值。

    let {length : len} = 'hello';
    len // 5
    
  4. 数值和布尔值的结构赋值
    解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。数值和布尔值的包装对象都有toString属性,其属性值用于解构赋值。 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

  5. 函数参数的解构赋值
    函数的参数也可以使用解构赋值。

    function add([x, y]){
     return x + y;
    }
    
    add([1, 2]); // 3
    

    上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。 对于函数内部的代码来说,它们能感受到的参数就是x和y。