变量的解构赋值
数组的解构赋值
基本用法
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这称为解构
。
以前为变量赋值只能直接指定值:
1 | var a = 1; |
ES6允许写成这样:
1 | var [a,b,c] = [1,2,3]; |
可以表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同左边的变量就会被赋予对应的值。
1 | let [h,...t] = [1,2,3,4] |
如果解构不成功,变量的值就等于undefined。
1 | var [foo] = []; |
这两种情况都是属于解构不成功,foo的值都等于undefined。
另一种情况是不完全解构,即等号左边的模式只匹配一部分的等号右边的数组。这种情况下,解构也是可以成功
1 | let [x,y] = [1,2,3] |
都属于解构不完全但是可以成功。
如果等号的右边不是数组(或者说不是可遍历的解构)那么将会报错
1 | //报错 |
上面的表达式都会报错,因为等号右边的值,要么转为对象以后不具备Iterator接口,要门本身就不具备Iterator接口。
解构赋值不仅适用于var命令,还适用于let和const命令。对于set结构也可以使用数组的解构赋值。
1 | let [x,y,z] = new Set([1,2,3]) |
解构赋值允许指定默认值。
1 | var [foo = true] - []; |
注意:ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的
默认值可以引用解构赋值的其他变量,但是该变量必须已经声明。
1 | let [x=1,y=x] = []; //x=1;y=1 |
对象的解构赋值
解构不仅可以用于数组,也可以用于对象。
1 | var {foo,bar} = { foo:'1',bar:'a' } |
对象的解构赋值和数组有一个重要的不同:数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
如果变量名与属性名不一致,必须写成下面这样。
1 | var { foo: baz } = { foo:'1',bar:'a' } |
这实际上说明,对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。
1 | var { foo: foo, bar: bar } = { foo:'1',bar:'a' }; |
也就是说,对象的解构赋值的内部机制,是先找到同名属性,真正被赋值的是后者而不是前者。
对象跟数组一样也可以用来嵌套解构赋值。
对象的解构也可以指定默认值。默认值生效的条件是对象的属性值严格等于undefined
如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错:
1 | var {foo:{bar}} = {bar:'baz'}; |
foo属性对应一个子对象,该子对象的bar属性解构时会报错,因为foo这个时候等于undefined,再取子属性就会报错。
如果要将一个已经声明的变量用于解构赋值,需要非常小心
1 | //错误写法 |
因为js引擎会将{x}理解成一个代码块,从而发生语法错误。只有不讲大括号写在行首,避免js将其解释为代码块才能解决这个问题。
解构赋值允许,等号左边的模式之中,不放置任何变量名。
1 | //毫无意义的写法 |
对象的解构赋值可以很方便的将现有对象的方法复制到某个变量
1 | let { log, sin, cos } = Math |
会将Math对象的对数、预先】正弦三个方法赋值到对应的变量上。
字符串的解构赋值
字符串也可以解构赋值,这是因为字符串被转换成了一个类似数组的对象
1 | const [a,b,c,d,e] = 'hello' |
类似数组的对象都有一个length属性,因此,还可以对这个属性解构赋值
1 | let { length: lem} ='hello' |
数值和布尔值的解构赋值
解构赋值是如果等号右边时数值和布尔值,会先转为对象。
1 | let {toString: s} = 123; |
由于数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
解构赋值的规则时,只要等号右边的值不是对象,就先将其转换为对象,由于undefined和null无法转换为对象,所以对他们进行解构赋值都会报错。
函数参数的结构辅助
函数的参数也可以使用解构赋值
1 | function add([x,y]{ |
函数add的参数实际上不是一个数组,而是通过解构得到的变量x和y。
1 | [[1,2],[3,4]].map(([a,b]) => a+b) |
函数参数的解构也可以使用默认值。
圆括号问题
ES6的规则是只要有可能导致解构的歧义,就不得使用圆括号。
以下三种解构赋值不得使用圆括号
1、变量声明语句中,模式不能带有圆括号
1 | //全部报错 |
2、函数参数中,模式不能带有圆括号
函数参数也属于变量声明,因此不能带有圆括号
1 | //报错 |
3、不能将整个模式,或嵌套模式中的一层,放在圆括号这
1 | ({p:a}) = {p:42} |
可以使用圆括号的情况
只有一种情况:赋值语句的非模式部分,可以使用圆括号
1 | [(b)] = [3] |
赋值语句而不是声明语句,赋值语句可以执行,声明语句不可以执行。
用途
- 交换变量的值
[x,y] = [y,x]
,代码简洁易读语义清晰 - 从函数返回多个值。
函数只能返回一个值,如果要返回多个值,只能将他们放在数组或者对象里返回。有了解构赋值,取出这些值就非常方便。1
2
3
4
5
6
7
8
9
10
11
12//返回一个数组
function example(){
return [1,2,3]
}
//返回一个对象
function example(){
return {
foo: 1,
bar: 2
}
} - 函数参数的定义
解构赋值可以方便的将一组参数与变量名对应起来1
2
3
4
5
6
7//参数是一组有次序的值
function f([x,y,z])
f([1,2,3])
//参数是一组无次序的值
function f({x,y,z})
f({z:1,x:2,y:3}) - 提取JSON数据
- 函数参数的默认值
- 遍历Map解构
任何部署可iterator接口的对象,都可以用for…of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。 - 输入模块的指定方法
加载模块时,往往需要指定哪些输入方法,解构赋值使得输入语句非常清晰。const { s,s1 } = require('xxx')