前言
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) console.log(bar) console.log(baz)
|
当左右不匹配的情况
正常情况下,我们的左右都应该是相同的,但是有时候我们也会遇到一些其他情况
1 2 3 4 5
| let [x, y, m, n] = [1, 2, 3] console.log(x) console.log(y) console.log(m) console.log(n)
|
这种情况下就会解构不成功,相当于变量只定义未初始化,他的值是undefined
1 2 3 4 5
| let [x, y, m, n] = [1, 2, 3, 4, 5] console.log(x) console.log(y) console.log(m) console.log(n)
|
这种情况下,属于不完全结构,但是可以成功。
默认值
1 2 3
| let [x, y = 10] = [1, 2] console.log(x) console.log(y)
|
1 2 3
| let [x, y = 10] = [1] console.log(x) console.log(y)
|
1 2 3
| let [x, y = 10] = [1, undefined] console.log(x) console.log(y)
|
ES6 内部使用严格相等运算符(===)判断一个位置是否有值,所以,如果一个数组成员不严格等于undefined
,默认值是不会生效的。
1 2
| let [x = 1] = [null] console.log(x)
|
比如上面的代码中,如果一个数组成员是null
,默认值就不会生效,因为null
并不严格等于 undefined
。
- 如果默认值是一个表达式(函数),那么这个表达式是
惰性求值
的,即只有在用到的时候才会求值,如果能取到值,这个表达式根本不会执行。
1 2 3 4 5 6 7
| function f() { console.log('aaa') }
let [x, y = f()] = [1, 2] console.log('x') console.log('y')
|
省略的情况
这种情况,我们只要将对应的位置空出来就可以了
1 2 3
| let [x, , y] = [1, 2, 3] console.log('x') console.log('y')
|
不定参数赋值
比如说下面的代码中,我们想把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') console.log('y')
|
这个...
是一个拓展运算符。
如果等号的右边不是数组(或者严格来说不是可遍历的结构),那么就会报错,比如:
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)
|
1 2 3
| let { name: newName, age: newAge } = { name: '张三', age: 10 } console.log(newName) console.log(newAge)
|
默认值
与数组类似,对象的解构赋值我们同样可以设置默认值。默认值生效的条件也与数组类似:对象的属性值必须严格等于undefined
1 2
| let { x = 3 } = {} console.log(x)
|
1 2 3
| let { x, y = 5 } = { x: 1 } console.log(x) console.log(y)
|
嵌套结构的对象
与数组类似,结构也可以用于嵌套结构的对象
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 3 4 5
| let arr = [1, 2, 3]
let { 0: first, 2: last } = arr console.log(first) console.log(last)
|
字符串、数值和布尔值的解构赋值
字符串也可以进行解构赋值,如果是使用数组这样的形式,此时字符串被转换成了一个类似数组的对象.
1 2 3
| const [a, b] = 'hi' console.log(a) console.log(b)
|
类数组对象都会有一个 length 属性,因此,还可以对这个属性解构赋值
1 2
| let { length: len } = 'hehe' console(len)
|
使用对象的形式,如果等号右边不是对象,默认转为对象
1 2
| let { a } = 1 console.log(a)
|
1 2
| let { __proto__: a } = 1 console.log(a)
|
函数参数的解构赋值
函数的参数也可以使用解构赋值,也可以设置默认值。
1 2 3 4
| function getA([a, b, c, ...d]) { console.log(a, b, c, d) } 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 }) getB({ name: 'foo' }) getB({}) getB()
|
1 2 3 4 5 6 7
| function getC({ name, age } = { name: 'bar', age: 20 }) { console.log(name, age) } getC({ name: 'foo', age: 10 }) getC({ name: 'foo' }) getC({}) getC()
|
这是因为,下面的代码是为函数的参数指定默认值,而不是为变量指定默认值,所以会得到与上面的代码不同的结果
参考资料:
《ES6 标准入门》(第 3 版) 阮一峰著