数组的解构赋值
基本用法
ES6允许按照一定模式数组和对象中提取值,然后对变量进行赋值,这被称为解构
let [a,b,c] = [1,2,3]
上面的代码表示,可以从数组中提取值,按照对应位置对变量赋值
本质上,这种写法属于"模式匹配",只要等号两边 的模式相同,左边的变量就会被赋予对应的值。
eg:
let [a,[b,c]] = [1,[2,3]]
console.log(a,b,c) //1,2,3
let [head,...foot] = [1,2,3,4]
//head : 1,foot : [2,3,4]
如果解构不成功,则变量的值就等于undefined
比如下面两种情况foo
的值都为undefined
let [foo] = []
let [bar,foo] = [1]
另外一种情况是不完全解构即等号左边的模式只匹配一部分的等号右边的数组,这种情况下解构依然可以成功
let [a,b] = [1,2,3]
//a:1,b:2
let [a,[b],c] = [1,[2,3],4]
//a:1,b:2,c:4
如果等号的右边不是数组,那么就会报错。
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};
上面的语句都会报错,因为等号右边的值或是转为对象以后不具备Iterator接口,或者是本身就不具备Iterator接口
对于Set结构,也可以使用数组中的解构赋值
事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值
默认值
解构赋值允许指定默认值
let [foo = true] = []
console.log(foo) //true
let [x,y = 2] = [1]
console.log(x,y) //1,2
let [x,y = 2] = [1,3]
console.log(x,y) //1,3
let [x,y = 2] = [undefined,1]
console.log(x,y) //undefined,1
ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值不会生效
let [a,b = 2] = [3,null]
console.log(a,b) //3,null
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到时才会求值
let [a,b = f()] = [3]
console.log(a,b)
function f (){
console.log("b未取到值")
return 5
}
//运行结果:
// b未取到值
// 3,5
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
对象的解构与数组有一个重要的不同:数组的元素是按次序排列的,变量的取值是由它的位置决定的,而对象的属性没有次序,变量必须与属性同名才能取到正确的值。
let {bar,foo} = {
bar:"asd",
foo:123
}
console.log(bar,foo)//asd,123
let {baz} = {
bar: "asd"
}
console.log(baz) //undefined
如果变量名与属性名不一致,必须写成下面这样:
var {foo : baz} = { foo :1}
console.log(baz) //1
console.log(foo) //error:foo is not defined
注意: 对象的解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量。真正被赋值的是后者,而不是前者。上面的代码中foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。
这里的foo : baz
写法需要特殊记一下,前面为模式,后面为需要赋值的变量
与数组一样,对象的解构赋值也可以用于嵌套解构的对象
let obj = {
p : ['hello',{y : 'world'}]
}
let {p : [ x, { y } ] } = obj
console.log(x,y) //hello world
对象的解构也可以指定默认值,和数组的解构一样,默认值生效的条件是对象的对应属性值严格等于undefined
let { a = 3 } = { a : undefined }
console.log(a) //3
let { x : y = 3 } = {}
console.log(y) //3
解构赋值另外需要注意的一个地方就是写法,如果想将已声明的变量用于解构赋值,像下面这样写是会报错的
let a;
{a} = {a : 5}
//SyntaxError: syntax error
这是因为JS引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将{
写在行首,避免JS将其解释成代码块,才能解决这个问题。常见的解决方法是给这个{}
加上()
let a;
({a} = {a : 5})
console.log(a) //5
对象的解构赋值可以很方便的将现有对象的方法复制到某个变量。
let { log , sin , cos } = Math ;
上面的代码将Math
对象的对数、正弦、余弦三个方法赋值到对应的变量上,使用起来就会方便很多。
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
let arr = [1,2,3]
let { 0 : first , [arr.length - 1] : last} = arr
console.log(first,last)//1 3
字符串的解构赋值
字符串也可以解构赋值,这是因为此时字符串被转换成了一个类似数组的对象
(我个人一直把字符串当成一个特殊的数组,但是字符串是字符串,数组是数组,不可混淆)
let [a,b,c,d] = "word"
console.log(a,b,c,d) // w o r d
类似数组的对象都有一个length
属性,因此还可以对这个属性进行解构赋值。
let {first : a} = "abcde"
console.log(a) //5
数值和布尔值的解构赋值
解构赋值的规则是,如果等号右边时数值和布尔值,则会先转为对象。
let { toString : a} = 1;
console.log(a) //[Function:toString]
由于undefined
和null
无法转为对象,所以对其进行解构赋值时会报错。
函数参数的解构赋值
function add([a ,b]){
return a + b
}
console.log(add([1,2])) //3
//相当于 let [a,b] = [1,2]
与常规的数组的解构赋值一样,也可以使用默认值,传入undefined
就会出发函数参数的默认值
function add([a ,b = 10]){
return a + b
}
console.log(add([1,undefined])) //11
解构赋值语法的圆括号问题
解构赋值很方便,但时解析起来并不容易。对于编译器来讲,一个式子到底是模式还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才知道。
由此带来的问题是,如果模式中出现圆括号该怎么处理?ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
所以,只要有可能,就不要在模式中放置圆括号
可以使用圆括号的情况
可以使用圆括号的情况只有一种:赋值语句的非模式部分可以使用圆括号。
[(a)] = [1]
({b : (c) } = {b : 1})
[parseInt.prop] = [3]
解构赋值的用途
1.交换变量的值
let a = 1
let b = 2
[ a,b ] = [ b,a ]
2.从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或者对象里面返回,有了解构赋值,取出这些值就非常方便了。
function back(){
return [1,2,3]
}
let [a,b,c] = back()
console.log(a,b,c) //1 2 3
3.函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来
//参数是一组有次序的值
function add ([a,b,c]){
return a + b + c
}
add([1,2,3])
//参数是一组无次序的值
function add ({a,b,c}){
return a + b + c
}
add({a : 1, b : 2 ,c : 3})
4.提取JSON数据
解构赋值对提取JSON对象中的数据尤其好用,这个在从接口拿到数据后使用数据非常方便
let jsonData = {
id : '42',
me : 'zs'
}
let {me,id} = jsonData
console.log(me,id)
5.函数参数的默认值
function add([a ,b = 10]){
return a + b
}
console.log(add([1,undefined])) //11
6.遍历Map结构
任何部署了Iterator
接口的对象都可以用for ... of
循环遍历。Map
结构原生支持Iterator
接口,配合变量的解构赋值获取键名和键值就非常方便。
let map = new Map()
map.set('first','firstName')
map.set('last','lastName')
for(let [key,value] of map){
console.log(key + '为' + value) //first为firstName
//last为lastName
}
7.引入模块的指定方法
加载模块时,往往需要指定输入的方法,解构赋值使得输入语句非常清晰。
const {someMethod,otherMethod} = require("methods")