定义
解构赋值是什么?
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
以前,为变量赋值,只能直接指定值。比如:
let a = 1
let b = 2
let c = 3
现在,ES6允许这样:
let [a, b, c] = [1, 2, 3]
或是这样:
let {a, b, c} = {a: 1, b: 2, c: 3}
上面三种方法的结果都是一样的。
本质上,后两种通过解构赋值的写法属于“模式匹配”,只要判定了等号两边的模式相同(解构),左边的变量就会被赋予对应的值(赋值)。ES6里大致有如下几种解构赋值的情况
数组的解构赋值
上面的第二个例子就是数组的解构赋值,这是刚好完全解构的情况。还有两种不完全解构的情况:
let [a, b, c] = [1, 2]
c // undefined
以及
let [a, b] = [1, 2, 3]
// 右边的3是多余的
let [ , , third] = ["foo", "bar", "baz"]
third // "baz"
从第一个例子可以看到当左边的c没有被解构成功时(即右边的数据结构没有与之对应的部分),那么这个变量的值为undefined
,我们知道某个值为undefined
的时候是有两种情况的,一是被声明了但是没有被赋值,二是显式的赋值为undefined
;那究竟是哪种情况呢?
const [d]=[]
d // undefined
可以知道是第二种情况,没被解构成功的变量会被显式赋值为undefined
当数组内部有嵌套时,解构依然能正常进行:
let [foo, [[bar], baz]] = [1, [[2], 3]]
foo // 1
bar // 2
baz // 3
let [a, [b, c], d] = [1, [2, 3], 4]
a // 1
b // 2
c // 3
d // 4
除此之外还允许为解构赋值的变量设定默认值,即变量在解构后没有被赋到值(严格来说应该是被赋值为undefined)的情况下赋予该变量为某个默认值:
let [foo = true] = []
foo // true
let [x, y = 'b'] = ['a']
// x='a', y='b'
let [x, y = 'b'] = ['a', undefined]
// x='a', y='b'
let [x = 1] = [undefined]
x // 1
let [x = 1] = [null]
x // null
由以上几个例子可以知道,在变量有默认值时,只有当解构完之后赋的值为undefined
时默认值才会生效,否则默认值会被解构之后的赋值所覆盖。
此外,当默认值为表达式时,表达式的求值是惰性的:
function f() {
console.log('aaa')
}
let [x = f()] = [1]
x //1,同时f()不会被执行
因为x被解构赋值为1,所以默认值不会生效,于是f()
也就没有必要执行,它也确实没有被执行,因为如果执行的话会打印出aaa
。当然,如果右边的[1]
换成[]
或者[undefined]
的话,f()
就会被执行。
默认值还可以引用解构赋值的其他变量,但前提是该变量必须已经声明,即被引用变量必须排在引用变量的左边:
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError
上面最后一个表达式之所以会报错,是因为x用到默认值y
时,y
还没有声明。
对象的解构赋值
赋值的规则与数组的解构赋值本质上是一样的,都是解析两边的等号两边的模式,然后将对应位置上的数据关联起来。但与数组不同的时,对象是通过键来区分其内部成员,而非数组里的位置,所以对象的解构与变量的位置无关:
let {a, b} = {a: 1, b: 2}
//a=1 b=2
let {b, a} = {a: 1, b: 2}
//a=1 b=2
我们知道(其实并不知道,好像是ES6才加的属性写法。。。。)
let a = 1
let obj = {a}
//obj ={a: 1}
let a = 1
let obj = {a: a}
//obj ={a: 1}
所以let {a, b} = {a: 1, b: 2}
其实是let {a: a, b: b} = {a: 1, b: 2}
的简写。由于这里刚好左边的变量名与右边的键名一致,可以这样写,如果我们想换个其他的变量名就得把后面一部分写出来了:
let {a: c, b: d} = {a: 1, b: 2}
c // 1
d // 2
a // Uncaught ReferenceError: a is not defined
b // Uncaught ReferenceError: b is not defined
这里a
和b
并非变量,而是用来匹配的模式,所以没有定义;如果同时想定义a
和b
可以这样写let {a, b, a: c, b: d} = {a: 1, b: 2}
,即等价于let {a: a, b: b, a: c, b: d} = {a: 1, b: 2}
,这也是对象解构与数组解构不同的地方,数组解构是通过数组的下标来匹配,而一个下标只能容下一个变量,对象就不一样了,一个对象的键名可以容得下多个变量。
与数组一样,解构也可以用于嵌套结构的对象,直接看一个例子:
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p//Uncaught ReferenceError: p is not defined,因为p只是匹配的模式
字符串的解构赋值
字符串也可以解构赋值,这是因为此时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
const {length} = 'hello'
length //5
同时,作为三个基本包装类型之一,字符串还可以调用String
对象的一些方法,于是:
let {indexOf} = 'hello'
indexOf === String.prototype.indexOf // true
可以知道,解构赋值时,如果等号右边是数值、字符串和布尔值三种基本包装类型时,则会将之先转为对象。同理,很容易可以推测另外两种基本包装类型的的解构赋值,不再赘述。
函数参数的解构赋值
参数的解构赋值本质数组的解构赋值或者对象的解构赋值,将一个数组或者对象作为参数传进一个函数,真正能被函数感知是参数是解构之后的被赋值的变量,比如在写React的无状态组件时我们可能会这样写:
ComponentA = (props) => {
return (
<div>props.prop1 + props.prop2</div>
)
}
这里props
是组件实例化时传入的一个包含所有prop的对象,那我们就可以这样写:
ComponentA = ({prop1, prop2}) => {
return (
<div>prop1 + prop2</div>
)
}
这样就不必每次用到prop时都加个props.
了,数组的情况也差不多。
再说下函数参数解构赋值有默认值的情况,先看一个例子:
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
这里{x = 0, y = 0} = {}
的意思是赋参数x
和y
在解构时的的默认值为0的同时设置函数move
的默认参数为{}
,当什么都没传给函数move
时,就等于把{}
传给函数,此时x
和y
的有一个解构的默认值都是0,当传入函数的值解构后可以对x
或y
赋值(非undefined
)时,则x
或y
为新赋的值。
另一个例子:
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
这里{x, y} = { x: 0, y: 0 }
的意思是并不单独为x
和y
赋默认值,只给函数的参数赋默认值为{ x: 0, y: 0}
,意即当什么都没传入的时候,等于将{ x: 0, y: 0}
传给了move
函数;所以当将{}
作为参数传入的时候,{x, y}={}
解构之后x
, y
的值都为undefined
。
总之,第一个例子是:对象解构赋值的默认值(x=0;y=0
) + 函数参数的默认值({}
);第二个例子只有一个函数参数的默认值({x: 0, y: 0}
)。
用途
本质都是对象解构赋值或者数组解构赋值
其余详见
ECMAScript 6 入门-阮一峰
与之相关的知识点还有Iterator 和 for...of 循环和Set 和 Map 数据结构
网友评论