javascript笔记之ES6对象的解构赋值

ES6入门教程

ES6允许按照一定的模式,从数组对象和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

三个规则

  1. 如果等号右边不是对象,会尝试转换为对象
  2. 只有当等号右边对应的属性值严格等于undefined的时候,等号左边的默认值才能生效

    如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

    1
    2
    3
    4
    5
    function f() {
    console.log('aaa');
    }

    let [x = f()] = [1];
  3. 数组的解构赋值要求等号右边的对象具备Iterator接口,即只要某种数据结构具备Iterator接口,都可以采用数组的形式解构。

    以下数据结构原声具备Iterator接口:

    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象
    • Generator 函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      function* fibs() {
      let a = 0;
      let b = 1;
      while (true) {
      yield a;
      [a, b] = [b, a + b];
      }
      }

      let [first, second, third, fourth, fifth, sixth] = fibs();
      sixth // 5

圆括号问题

解构赋值虽然很方便,但是对于编译器来说,一个式子到底是模式,还是表达式,必须解析到(或解析不到)等号才能知道。

由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。

但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。因此,建议只要有可能,就不要在模式中放置圆括号

使用圆括号

只有一种情况可以使用圆括号:赋值语句的非模式部分,可以使用圆括号。

1
2
3
4
5
6
// 模式是取数组的第一个成员,跟圆括号无关
[(b)] = [3]; // 正确
// 模式是p
({ p: (d) } = {}); // 正确
// 同上面第一句
[(parseInt.prop)] = [3]; // 正确

上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性质一致。

在赋值语句中,等号左边数组的非下标部分和对象的非属性部分可以使用圆括号

不适用圆括号

以下三种解构赋值不得使用圆括号。

  1. 变量声明语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 全部报错
    let [(a)] = [1];

    let {x: (c)} = {};
    let ({x: c}) = {};
    let {(x: c)} = {};
    let {(x): c} = {};

    let { o: ({ p: p }) } = { o: { p: 2 } };
  2. 函数参数

    函数参数也属于变量声明,因此不能带有圆括号。

    1
    2
    3
    4
    // 报错
    function f([(z)]) { return z; }
    // 报错
    function f([z,(x)]) { return x; }
  3. 赋值语句的模式

    1
    2
    3
    // 全部报错
    ({ p: a }) = { p: 42 };
    ([a]) = [5];

用途

  1. 交换变量的值

    1
    2
    3
    4
    let x = 1;
    let y = 2;

    [x, y] = [y, x];
  2. 函数参数的定义

    解构赋值可以方便地将一组参数与变量名对应起来。

    1
    2
    3
    4
    5
    6
    7
    // 参数是一组有次序的值
    function f([x, y, z]) { ... }
    f([1, 2, 3]);

    // 参数是一组无次序的值
    function f({x, y, z}) { ... }
    f({z: 3, y: 2, x: 1});
  3. 提取 JSON 数据

    解构赋值对提取 JSON 对象中的数据,尤其有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let jsonData = {
    id: 42,
    status: "OK",
    data: [867, 5309]
    };

    let { id, status, data: number } = jsonData;

    console.log(id, status, number);
    // 42, "OK", [867, 5309]
  4. 函数参数的默认值

    指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。

  5. 遍历map结构

    任何部署了Iterator接口的对象,都可以用for…of循环遍历。Map 结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。

    1
    2
    3
    4
    5
    6
    7
    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');

    for (let [key, value] of map) {
    console.log(key + " is " + value);
    }
  6. 提取数组的头尾的值

    数组也是对象的一种,下面这种方法对于数组和类数组对象尤其有用

    1
    2
    3
    4
    5
    let arr = [1, 23, 4, 5, 5, 6];

    let { 0: first, [arr.length - 1]: last } = arr;

    console.log(first, last);