携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
数组的解构赋值
用法
ES6允许按照一定的模式,从对象和数组中提取值,对变量进行赋值,这就被称为结解构。
// 以前
let a = 1
let b = 2
let c = 3
console.log(a, b, c)//1 2 3
let [a, b, c] = [1, 2, 3]
console.log(a, b, c)//1 2 3
按照对应位置,从数组中提取值,对变量赋值,这种写法属于模式匹配。只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [a,[[b],c]] = [1,[[2],3]]
console.log(a, b, c) //1 2 3
let [d,e,f] = [4]
console.log(d, e, f) //4 undefined undefined
let [g,h,...r] = [5]
console.log(g,h,r) //5 undefined []
let [k,,l] = [6, 7, 8]
console.log(k,l) //6 8
如果解构不成功,变量的值就会等于undefined
不完全结构
属于不完全结构,这种情况下,也会解构成功
let [a,b,c] = [1,2]
console.log(a, b)//1 2
let [d,[e,f],g] = [3,[4],5]
console.log(d,e,f)//3 4
如果等号右边不是数组,那么就会报错
let [a] = 1//1 is not iterable
let [b] = false//false is not iterable
let [c] = undefined//undefined is not iterable
let [d] = {}// {} is not iterable
let [e] = null//null is not iterable
let [f] = NaN //NaN is not iterable
new Set结构的数组也是可以解构的。事实上,只要某种数据具有Iterator接口,都可以采用数组形式的结构赋值。
let [a, b, c] = new Set([1, 2, 3])
console.log(a, b, c) //1 2 3
generotor函数
function* fibs() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
默认值
解构赋值允许指定默认值
let [a=true] = []
console.log(a) //true
let [b,c=2] = [1]
console.log(b, c) //1,2
let [d,e=3] = [4,undefined]
console.log(d,e)//4,3
注意:ES6内部严格使用undefined,判断一个位置是否有值。所有只有一个值严格等于unedfined,默认值才会生效。如果一个数组成员是null,默认就不会生效,因为null不严格等于undefined。
let [a=1] = [null]
console.log(a) //null
let [b=2] = [undefined]
console.log(b) //2
如果默认值是一个表达式,是惰性的,在用到他的时候才会执行。
function f(){
console.log('来了')
}
let [a=f()]=[1]
console.log(a) //1
function f(){
console.log('来了')
}
let [a=f()]=[undefined]
console.log(a) //来了 undefined
默认值可以解构赋值的其他变量,前提是必须这个变量已经被解构
let [a=1,b=a] = []// a=1 b=1
let [a=1,b=a] = [2]//a=2 b=2
let [a=1,b=a] = [3,4] //a=3 b=3
let [a=b,a=1] //ReferenceError: a is not defined
对象的解构赋值
对象和数组的解构赋值不同点:数组是按照顺序排列;对象取值没有顺序之分,变量与属性名相同就可取到值。
let {a,b} = {a:1,b:2}
console.log(a, b) // 1,2
如果对象没有解构到值,则为undefined
let {a,b,c} = {a:1,b:2}
console.log(c) // undefined
将现有对象的方法赋值到某个变量上
const {log} = console
log('hello')//hello
变量值和属性值不一样,处理方法如下
let {a:b}={a:1}
console.log(b)//1
对象赋值的内部机制,先找到同名属性,然后再赋值给赋值给对应的变量。真正被赋值的是后者,并不是前者。对象解构赋值的简写:let {a:a,b:b} = {a:1,b:2}
对象嵌套的解构赋值
let obj = {
p:[
'hello',
{
y:'world'
}
]
}
let {p:[x,{y}]} = obj
console.log(x, y)//hello world
console.log(p)// ReferenceError: p is not defined
注意:p是模式不是变量,因此不会被解构赋值。应写成这样:
let obj = {
p:[
'hello',
{
y:'world'
}
]
}
let {p:[x,{y}]} = obj
console.log(x, y)//hello
console.log(p)// ['hello',{y:world}]
let a = {
b:{
c:{
d:1,
e:2
}
}
}
let {b,b:{c},b:{c:{d}}} = a
console.log(b)//c{d:1,e:2}
console.log(c) //{d:1,e:2}
console.log(d)//1
以上,b,c是模式,d是变量
以下是嵌套赋值:
let obj = {};
let arr = [];
({foo: obj.prop, bar: arr[0]} = {foo: 1, bar: true})
console.log(obj, arr)////{prop:123} [true]
等号左边对象的a属性,对应一个子对象。该子对象的b属性,解构时会报错。因为a属性的值为undefined,再取子属性就会报错。
//报错
let {a: {b}} = {c: 'baz'};
console.log(a) //Cannot read properties of undefined (reading 'b')
对象的解构赋值可以取到继承属性
let a = {}
let b = {aa:1}
Object.setPrototypeOf(a, b)
console.log(a, aa.aa)//{} 1
默认值
let {a=1}={}
console.log(a)//1
let {b,c=2} = {b:3}
console.log(b);//3
let {d:e=4}={d:5}
console.log(e)//5
默认值生效的条件是,对象的属性值严格等于undefined。null和undefined不严格相等
let {a=3}={a:undefined}
console.log(a)//3
let {b=4}={b:null}
console.log(b)//null
注意:
a.如果将一个已经声明的变量解构赋值,需注意
let a;
[a] = [1]
console.log(a)//1
let a;
{a}={a:1} //SyntaxError: Unexpected token '='
({a}={a:1}) //1解决办法
console.log(a)//1
因为JS引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免javascript将其理解为代码块,才能解决这个问题。
b.解构赋值允许左边的模式之中,不放置任何变量名。因此可以写出非常古怪的赋值表达式。虽然表达式毫无意义,但是语法是合法的,可以执行。
({}=[true,false]);
({}='abc');
({}=[]);
c.由于数组是特殊的对象,因此可以对数组进行对象属性的解构。解构的是下标。
let arr = [1, 2, 3];
let {0:first, [arr.length-2]:last} = arr
console.log(first)//1
console.log(last)//2
字符串的解构赋值
字符串也可以进行解构赋值,因此,字符串被转换成类似数组的对象。
const [a,b,c,d,e] = 'hello'
console.log(a,b,c,d,e)
类数组的对象都有一个length属性,因此还可以对这个属性解构赋值
const {length:len} = 'hello'
console.log(len)//5
数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值或者布尔值的时候,会先转换为对象。
let {toString:s} = 123
//Number === Number.proptotype.toString
console.log(s) //ƒ toString() { [native code] }
let {toString:a} = true
console.log(a) //ƒ toString() { [native code] }
解构赋值的规则是,只要等号右边不是对象和数组,就先将其转为对象。由于undefined和null无法转为对象,所以对他们进行解构赋值会报错。
let {toString:s} = undefined
console.log(s) //Cannot destructure property 'toString' of 'undefined' as it is undefined.
函数参数的解构赋值
let b = [[1, 2], [3, 4]].map(([a,b]) =>{
return a+b
})
console.log(b) //3 7
函数参数的解构也可以使用默认值
function add({a=1, b=2}={}){
return [a, b]
}
console.log(add({a:3, b:4}))//[3, 4]
console.log(add({a:9})) //[9, 2]
console.log(add()) //[1, 2]
console.log(add({})) //[1, 2]
函数move是一个对象,通过对这个对象进行解构,得到变量x和y的值,如果解构失败,x和y等于默认值
function add({a,b} = {a:1,b:2}) {
return [a, b]
}
console.log(add()) //[1,2]
console.log(add({a:3})) //[3,undefined]
console.log(add({}))//[undefined, undefined]
console.log(add({a:3, b:4}))
undefined会触发函数的默认值
[1, undefined, 3].map((x='yes') => x)//[1, 'yes', 3]
圆括号问题
ES6的规则是,只要有可能导致歧义的,就不得使用圆括号。但是,建议只要有可能,就不要在模式中防止圆括号。
用途
变量的解构赋值用途很多
- 交换变量的值.[x,y]=[y,x]
- 从函数中返回多个值
function add(){
return [1, 2, 3, 4]
}
const [a,b,c,d]=add();
console.log(a,b,c,d)// 1 2 3 4
function sum(){
return {obj1:'hello', obj2:'world'}
}
const {obj1,obj2} = sum()
console.log(obj1, obj2) //hello world
- 函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来
// 参数有次序
function fn([x,y,z]){}
fn([1,2,3])
// 参数是一组无次序的值
function fn({x,y,z}){}
fn({z:3, x:1, y:2})
- 提取json中的数据
let jsonData = {
id: 42,
status: 'ok',
data: [1, 2]
}
const {id,status,data:number} = jsonData
console.log(id, status, number)//42 'ok' [1, 2]
- 函数参数的默认值function fn(a=1){}
- 便利Map结构 任何部署了Iterator接口的对象,都可以用for...of遍历循环。map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就更加方便了
const map = new Map()
map.set('a',1)
map.set('b', 2)
for(let [key, value] of map){
console.log(key+' is '+value)
}
// 循环value值
for(let [,value] of map){
console.log(value)//1 2
}
- 输入模块的指定方法 加载模块时,往往需要指定哪些方法。解构赋值使得输入语句非常清晰。
import {aa, bb} from 'aa.js'