ES6+ 解构赋值

1. 前言

本节我们会学习到 ES6 中的解构赋值语法,它是 JavaScript 中的一种表达式。在本节中你可以了解以下内容:

  • 什么是解构赋值
  • 数组的解构赋值
  • 对象的解构赋值
  • 如何解构嵌套对象和数组

解构赋值就是分解一个数据的结构,并从这数据解构中提取数据的过程,它可以从数组中取出值赋给变量或是将对象中的属性提取出来赋给另一个对象的属性。解构的目的是为了简化提取数据的过程,增强代码的可读性。 把变量放在 [] 或者 {} 中来获取目标对象上的对应的值。

2. 数组的解构赋值

2.1 基本用法

数组的解构赋值是,通过新建一个数组,数组内的变量和目标数组是一一对应的,按照索引的方式去获取值,然后赋值给指定索引上的变量。在 ES6 之前,想获取数组中的值,需要通过 Array[index] 的方式来获取值,这种方式的好处在于可以获取数组上指定索引的值,如果我们想把数组的每一项的值都取出来,赋给变量,这样的写法就显得很笨拙:

var arr = [10, 20];
console.log(arr[0]);  // 10
console.log(arr[1]);  // 20

上面的取值方式是 ES6 之前的写法,有了 ES6 的解构赋值就简单了很多,下面看 ES6 解构赋值是怎么取数组的对应值的:

var [a, b] = [10, 20];
console.log(a);   // 10
console.log(b);   // 20

上面的例子中先声明一个数组,分别在数组的 0 和 1 的索引位置上放置变量 a 和 b,对应着需要获取数组的索引位置,这样就可以从数组当中取到我们想要的值了。

2.2 默认值

在解构一个未知的数组时,需要对未能取的值的变量赋一个默认值,为了防止从数组中取出一个值为 undefined 的对象,可以在表达式的左边的数组中为任意变量预设一个默认的值。

let [a=3, b=9] = [1];             // a=1 b=9
let [a, b = 1] = [10, ''];        // a=10, b=''
let [a, b = 1] = [10, undefined]; // a=10, b=1

Tips: 在 ES6 中,判断一个数组中是否有值,使用严格相等运算符(===)来进行判断,只有当一个数组成员严格等于 undefined,默认值才会生效。所以第三个 b 使用了默认值。

let [a = 1] = [null];             // a=null

我们知道 null==undefined 返回的是 true,null===undefined 返回的是 false。所以数组成员是 null,默认值就不会生效。

2.3 交换变量

在 ES6 之前如果我们想交换两个变量的值时,我们需要借助一个中间的值来过渡,下面是要交换 a、b 值的过程:

var a = 1;
var b = 4;
var m = a;
a = b;
b = m // a=4, b=1

在交换 a、b 值时,需要借助中间的变量来中转,使用 ES6 解构赋值就很简单:

var a = 1;
var b = 4;
[a, b] = [b, a] // a=4, b=1

2.4 跳过某项值使用逗号隔开

在解构数组时,可以忽略不需要解构的值,可以使用逗号对解构的数组进行忽略操作,这样就不需要声明更多的变量去存值了:

var [a, , , b] = [10, 20, 30, 40];
console.log(a);   // 10
console.log(b);   // 40

上面的例子中,在 a、b 中间用逗号隔开了两个值,这里怎么判断间隔几个值呢,可以看出逗号之间组成了多少间隔,就是间隔了多少个值。如果取值很少的情况下可以使用下标索引的方式来获取值。

2.5 剩余参数中的使用

通常情况下,需要把剩余的数组项作为一个单独的数组,这个时候我们可以借助展开语法把剩下的数组中的值,作为一个单独的数组,如下:

var [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a);     // 10
console.log(b);     // 20
console.log(rest);  // [30, 40, 50]

在 rest 的后面不能有 逗号 不然会报错,程序会认出你后面还有值。...rest 是剩余参数的解构,所以只能放在数组的最后,在它之后不能再有变量,否则则会报错。

3. 对象的解构赋值

3.1 基本用法

对象的解构和数组基本类似,对象解构的变量是在 {} 中定义的。对象没有索引,但对象有更明确的键,通过键可以很方便地去对象中取值。在 ES6 之前直接使用键取值已经很方便了:

var obj = { name: 'imooc', age: 7 };
var name = obj.name;  // imooc
var age = obj.age;    // 7

但是在 ES6 中通过解构的方式,更加简洁地对取值做了简化,不需要通过点操作增加额外的取值操作。

var obj = { name: 'imooc', age: 7 };
var { name, age } = obj;  // name: imooc, age: 7

{} 直接声明 name 和 age 用逗号隔开即可得到目标对象上的值,完成声明赋值操作。

3.2 默认值

对象的默认值和数组的默认值一样,只能通过严格相等运算符(===)来进行判断,只有当一个对象的属性值严格等于 undefined,默认值才会生效。

var {a = 10, b = 5} = {a: 3};                 // a = 3, b = 5
var {a = 10, b = 5} = {a: 3, b: undefined};   // a = 3, b = 5
var {a = 10, b = 5} = {a: 3, b: null};        // a = 3, b = null

所以这里的第二项 b 的值是默认值,第三项的 null === undefined 的值为 false,所以 b 的值为 null。

3.3 重命名属性

在对象解构出来的变量不是我们想要的变量命名,这时我们需要对它进行重命名。

var {a:x = 8, b:y = 3} = {a: 2};

console.log(x); // 2
console.log(y); // 3

这里把 a 和 b 的变量名重新命名为 x 和 y。

3.4 剩余参数中的使用

在对象的解构中也可以使用剩余参数,对对象中没有解构的剩余属性做聚合操作,生成一个新的对象。

var {a, c, ...rest} = {a: 1, b: 2, c: 3, d: 4}
console.log(a);     // 1
console.log(c);     // 3
console.log(rest);  // { b: 2, d: 4 }

对象中的 b、d 没有被解构,通过剩余参数语法把没有解构的对象属性聚合到一起形成新的对象。

4. 解构字符串

当然字符串也是可以被解构的,字符串可以当作数组被解构。

const [a, b, c, d, e] = 'imooc';
console.log(a);   // "i"
console.log(b);   // "m"
console.log(c);   // "o"
console.log(d);   // "o"
console.log(e);   // "c"

字符串可以被看成类数组的东西,类似数组的对象都有一个 length 属性,字符串也可以被当作对象来解构,但是由于字符串方法只有 length 属性,所以只能解构出 length 的值。

let {length : len} = 'hello';
console.log(len); // 5

5. 解构复杂的数据结构

了解了数组和对象的解构,下面我们看下对于复杂数据解构应该如何去解构呢?看下面的例子:

var data = {
  a: 1,
  arr: [
    {
      b: 2
      c: 3
    }
  ]
}

var { 
  a: newA,
  arr: [
    {
      b: newB
    }
  ]
} = data;
console.log(newA, newB) // 1, 2

上面的例子中 data 的数据解构还算是比较复杂的,对于解构这种既有对象又有数组的数据解构,我们要先声明一个和目标对象有着相同的数据嵌套,才能去拆解目标对象。这里解构的是 data 中的 a 和 b 的值,而且把 a 和 b 重命名为 newA 和 newB。

6. 小结

本节讲解了 ES6 解构赋值的使用方法,总结下来一共有以下几点:

  1. 解构赋值一般针对对象和数组,如果解构对象是 undefined 或是 null 都会报错;
  2. 默认值的生效条件是,只有当解构的对象的值是严格模式下的 undefined 的情况下,默认值才会生效;
  3. 可以不借助中间变量来交换两个值;
  4. 在解构复杂的数据解构时,注意声明的对象要和目标的对象有着相同的解构形式,才能去解构目标对象。