前言

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

数组的解构赋值

基本用法

以前,我们为变量赋值一般都是直接指定值:

1
2
3
let a = 1
let b = 2
let c = 3

但是在 ES6 中,我们可以写成这样:

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

上面的代码表示可以从数组中提取值,按照对应的位置对应变量赋值,他的本质就是一种 模式匹配 ,只要等号两边的模式相同,左边的变量就会被赋予对应的值,比如这种嵌套结构:

1
2
3
4
let [foo, [[bar]], baz]] = [1, [[2]], 3]]
console.log(foo) // 1
console.log(bar) // 2
console.log(baz) // 3

当左右不匹配的情况

正常情况下,我们的左右都应该是相同的,但是有时候我们也会遇到一些其他情况

  • 变量数多于值或变量找不到对应的值
1
2
3
4
5
let [x, y, m, n] = [1, 2, 3]
console.log(x) // 1
console.log(y) // 2
console.log(m) // 3
console.log(n) // undefined

这种情况下就会解构不成功,相当于变量只定义未初始化,他的值是undefined

  • 变量数少于值
1
2
3
4
5
let [x, y, m, n] = [1, 2, 3, 4, 5]
console.log(x) // 1
console.log(y) // 2
console.log(m) // 3
console.log(n) // 4

这种情况下,属于不完全结构,但是可以成功。

默认值

  • 变量有对应的值时候,会被赋值
1
2
3
let [x, y = 10] = [1, 2]
console.log(x) // 1
console.log(y) // 2
  • 当变量没有对应的值,就会使用默认值
1
2
3
let [x, y = 10] = [1]
console.log(x) // 1
console.log(y) // 10
1
2
3
let [x, y = 10] = [1, undefined]
console.log(x) // 1
console.log(y) // 10

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

1
2
let [x = 1] = [null]
console.log(x) // null

比如上面的代码中,如果一个数组成员是null ,默认值就不会生效,因为null 并不严格等于 undefined

  • 如果默认值是一个表达式(函数),那么这个表达式是惰性求值 的,即只有在用到的时候才会求值,如果能取到值,这个表达式根本不会执行。
1
2
3
4
5
6
7
function f() {
console.log('aaa')
}

let [x, y = f()] = [1, 2]
console.log('x') // 1
console.log('y') // 2

省略的情况

这种情况,我们只要将对应的位置空出来就可以了

1
2
3
let [x, , y] = [1, 2, 3]
console.log('x') // 1
console.log('y') // 3

不定参数赋值

比如说下面的代码中,我们想把2,3,4,5的值,都赋值给 y ,我们就可以用到不定参数赋值

1
2
3
let [x, y] = [1, 2, 3, 4, 5]
console.log('x') // 1
console.log('y') // 2
1
2
3
let [x, ...y] = [1, 2, 3, 4, 5]
console.log('x') // 1
console.log('y') // [2, 3, 4, 5]

这个... 是一个拓展运算符。

如果等号的右边不是数组(或者严格来说不是可遍历的结构),那么就会报错,比如:

1
2
3
4
5
6
7
8
9

let [foo] = 1;

let [foo] = false;

let [foo] = NaN;

let [foo] = null;

对象的结构赋值

基本用法

类似于数组,结构都是一样的。但是值得注意的是,数组的元素是按次序排列的,变量的取值是由它的位置决定的,而对象的属性没有次序,变量必须与属性同名才能取到正确的值

  • 如果变量名和属性名是一样的,可以直接省略写法
1
2
3
let {name, age} = {name: '张三', age: 10}
console.log(name) // 张三
console.log(age) // 10
  • 如果变量名和属性名不一样,我们必须这样写:
1
2
3
let { name: newName, age: newAge } = { name: '张三', age: 10 }
console.log(newName) // 张三
console.log(newAge) // 10

默认值

与数组类似,对象的解构赋值我们同样可以设置默认值。默认值生效的条件也与数组类似:对象的属性值必须严格等于undefined

1
2
let { x = 3 } = {}
console.log(x) // 3
1
2
3
let { x, y = 5 } = { x: 1 }
console.log(x) // 1
console.log(y) // 5

嵌套结构的对象

与数组类似,结构也可以用于嵌套结构的对象

1
2
3
4
5
6
7
8
9
10
11
let obj = {
p: {
'hello',
{y: 'world'}
}
}

let {p, p: [x, {y}] } = obj
console.log(x) // hello
console.log(y) // world
console.log(p) // {'hello',{y: 'world'}}

注意:如果结构模式是嵌套对象,而且子对象所在的副属性不存在,那么将会报错。

1
2
3

let {foo: {bar}} = {bar: ’baz‘} //报错

对已经声明的变量用于解构赋值

错误的写法,这种情况下,{x} 会被理解成一个代码块,ES6 有块级作用域,就会导致语法报错:

1
2
let x;
{x} = {x: 1} // 报错

正确的写法 ,我们应该使用一个圆括号把它包起来:

1
2
let x
;({ x } = { x: 1 })

对数组进行对象属性的结构

数组的本质就是特殊的对象,因此可以对数组进行对象属性的结构赋值。

1
2
3
4
5
let arr = [1, 2, 3]

let { 0: first, 2: last } = arr
console.log(first) // 1
console.log(last) // 3

字符串、数值和布尔值的解构赋值

字符串也可以进行解构赋值,如果是使用数组这样的形式,此时字符串被转换成了一个类似数组的对象.

1
2
3
const [a, b] = 'hi'
console.log(a) // h
console.log(b) // i

类数组对象都会有一个 length 属性,因此,还可以对这个属性解构赋值

1
2
let { length: len } = 'hehe'
console(len) // 4

使用对象的形式,如果等号右边不是对象,默认转为对象

1
2
let { a } = 1
console.log(a) // undefined
1
2
let { __proto__: a } = 1
console.log(a) // Number {0, constructor: ƒ, …}

函数参数的解构赋值

函数的参数也可以使用解构赋值,也可以设置默认值。

1
2
3
4
function getA([a, b, c, ...d]) {
console.log(a, b, c, d) // 1 2 3 [4, 5]
}
getA([1, 2, 3, 4, 5])

在设置默认值的时候,你要注意下面两种写法,你会得到不一样的结果:

1
2
3
4
5
6
7
8
function getB({ name = 'bar', age = 10 } = {}) {
console.log(name, age)
}

getB({ name: 'foo', age: 20 }) // foo 20
getB({ name: 'foo' }) // foo 20
getB({}) // bar 10
getB() // bar 10
1
2
3
4
5
6
7
function getC({ name, age } = { name: 'bar', age: 20 }) {
console.log(name, age)
}
getC({ name: 'foo', age: 10 }) // foo 10
getC({ name: 'foo' }) // foo undefined
getC({}) // undefined undefined
getC() // bar 20

这是因为,下面的代码是为函数的参数指定默认值,而不是为变量指定默认值,所以会得到与上面的代码不同的结果

参考资料:

《ES6 标准入门》(第 3 版) 阮一峰著