JavaScript 解构
把数据结构分解开分别进行赋值
-
解构主体为数组或者对象。
-
解构源必须具备 Iterator 接口或者经过转换之后具备 Iterator 接口。
-
数组解构是按顺序进行解构(有序),对象是通过方法名或者属性名进行结构(无序)。
-
数组解构解构源会被转换为数组,对象解构解构源会被转换成对象。
-
解构对象时关键词解构会从当前实例持续向上访问原型链直到查不到返回 undefined。
语法
解构赋值的格式为,=左边为解构赋值的语法,=右边为初始化器,即一个对象或数组。
数组的解构赋值
数组的解构赋值是基于数组位置的,比如:
let [a,b] = [1,2] // 结果a等于1,b等于2
也可以通过解构改变变量的值。比如:
let a = 1, b = 2;
[a,b] = [100, 200];
当=左边与右边不完全匹配时,未能匹配到的变量会被赋值为undefined,比如:
let [a,b,c] = [1,2] //a为1,b为2,c为undefined
所以可以通过给一些变量指定默认值,以防止这种情况的发生。比如:
let [a,b,c=3] = [1,2] //a==1, b==2, c==3
注意:只有当在右边找不到对应的值或值为undefined时,才会使用默认值。
有时候,解构赋值中,你可能只关心一部分数据,这时可以通过占位符只给某些值赋值。比如:
let [a,,,b,,] = [1,2,3,4,5,6,7,8] //a==1 b==4
在解构赋值中,通过在变量前加…号,表示生成的变量为一个数组。比如:
let [a,,...b] = [1,2,3,4,5] //a == 1, b==[3,4,5]
上面展示的情况都是可以联合使用的,比如:
let [a,b=8,,..c] = [1,2,3,4,5,6] //a==1 b==2 c=[4,5,6]
对象的解构赋值
对象的解构赋值是基于属性的。比如:
let {name, age} = {
name: 'icode007',
age: 27
}
//name == 'icode007' age==27
与数组的解构赋值一样,对象的解构赋值一样给未能解构的变量赋值undefined,一样可以使用默认值。
当给已存在的变量解构赋值时,注意加()
let name, age;
({name, age} = {name: 'icode007', age: 27});
这是由于如果不加(),js会把左边看成一个代码块,会报错。 加了()后,整个变成了一个合法的表达式。
在上面的解构赋值中,变量名和对象中的属性名必须相同,只有这样,才能找到对应的要解构赋值的数据。 但如果我们想要给数据赋一个不同的名字呢? 也是有办法的。
let {name:myName, age: myAge} = {name: 'icode007', age: 27}
这样相应的名字和年龄就被赋值给myName和myAge了。
也可以同时使用默认值:
let {name:myName, age: myAge, jog: myJob = 'soft Engineer'} = {name: 'icode007', age: 27}
以上我们列举的对象的解构赋值的例子都非常的简单,但在实际开发中,JSON数据可能是非常复杂的,这时的解构赋值语法也可能变得复杂。比如:
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
let {loc: { start }} = node;
console.log(start.line);
console.log(start.column);
注意:每当有:出现在解构赋值中时,:左边的标识符表示要检查的位置,右边表示要赋值的目标,如果右边是{}或[]时,表示要赋值的变量在更深层次结构中。
上面的所有实例,如默认值,变量更名等特性都可能存在于一个解构赋值语句中。并且,数组的解构赋值与对象的解构赋值,也可以混合使用。这为我们从复杂的数据结构中提取相应数据提供了极大的便利。
函数参数的解构
函数参数的解构功能对于实现多参的函数是非常有用的。 比如
function setCookie(name, value, options){
options = options || {};
var secure = option.secure,
path = option.path,
domain = option.domain
;
//...
}
setCookie('type', 'js', {
secure: true,
expires: 60000
})
上面的函数是常用的实现多参函数的方式,name, value为必填参数,所有可选参数封装到options中,作为options的属性使用。 但上面的函数存在一个问题就是,你只看函数的定义,是无法知道到底可选参数的名称是什么的?你需要阅读函数代码,了解函数才能使用它。
使用函数参数解构则直观很多:
function setCookie(name, value, {secure, path, domain}){
//...
}
使用同样的使用方式可以调用这个函数。
但是这种写法有种问题是当只传入name和value参数时,会报错。
更好的写法是使用函数的默认参数。
function setCookie(name, value, {secure, path = "/", domain} = {}){
//...
}
注意事项
- 数组的解构赋值中,使用…rest的变量必须放在最后。
- 与普通的变量的赋值语句一样,解构赋值语句也是有值的,它的值就是=右边的内容。
最佳实践
在es6之前,交换两个变量的值,需要创建一个中间变量,类似这样
var a = 1, b = 2, temp;
temp = a; a = b; b = temp;
现在只需要一行代码: [a,b] = [b,a]
在实际开发中,数据解构是非常复杂的,使用对象属性去层层调用的语法非常不直观,通过解构赋值,可以让代码更加的直观与简洁。
数组的解构赋值中,有个小技巧。
let arr = [1,4,9,55,244];
let [...cloneArray] = arr;
console.log(cloneArray);
这样就实现了数组的浅复制,而在以前,数组的复制都是通过concat()方法来完成。
ES6 解构并非一个新功能,而是一个新的赋值语法,可以让您快速解压缩对象属性和数组中的值,并将它们分配给各个变量。
var profile = {name:'George', age:39,
hobby:'Tennis'}
var {name, hobby} = profile
这里我用解构快速提取 profile
对象的 name
和 hobby
属性 。
使用别名,你可以使用与你正在提取值的对象属性不同的变量名:
var profile = {name:'George', age:39,
hobby:'Tennis'}
var {name:n, hobby:h} = profile
嵌套对象解构
解构也可以与嵌套对象一起工作,我一直使用它来快速解开来自复杂的JSON请求的值:
var jsondata = {
title: 'Top 5 JavaScript ES6 Features',
Details: {
date: {
created: '2017/09/19',
modified: '2017/09/20',
},
Category: 'JavaScript',
},
url: '/top-5-es6-features/'
};
var {title, Details: {date: {created, modified}}} = jsondata
console.log(title, created, modified)
解构数组
数组的解构与在对象上的工作方式类似,除了左边的花括号使用方括号代替:
var soccerteam = ['George', 'Dennis', 'Sandy']
var [a, b] = soccerteam
console.log(a, b) //George Dennis
你可以跳过某些数组元素,通过使用逗号(,):
var soccerteam = ['George', 'Dennis', 'Sandy']
var [a, , b] = soccerteam
console.log(a, b) //George Sandy
对我而言,解构消除了传统方式提取和分配对象属性和数组值的所有摩擦。要充分掌握ES6解构的复杂性和潜力,请阅读"Getting to Grips with ES6: Destructuring".
默认和剩余参数(Default and Rest Parameters)
最后,我最想提出的ES6的两个特性是处理函数参数。几乎我们在JavaScript中创建的每个函数都接受用户数据,所以这两个特性在一个月中不止一次地派上用场。
默认参数(Default Parameters)
我们都使用过一下模式来创建具有默认值的参数:
function getarea(w,h){ var w = w || 10
var h = h || 15
return w * h
}
有了ES6对默认参数的支持,显式定义的参数值的日子已经结束:
function getarea(w = 10, h = 15) { return w * h } getarea(5) // returns 75
剩余参数(Rest Parameters)
ES6中的 Rest Parameters 使得将函数参数转换成数组的操作变得简单。
function addit(...theNumbers) { // get the sum of the array elements return theNumbers.reduce ((prevnum, curnum) => prevnum + curnum, 0) } addit(1, 2, 3, 4) // returns 10
通过在命名参数前添加3个点 ...
,在该位置和之后输入到函数中的参数将自动转换为数组。
没有 Rest Parameters, 我们不得不做一些复杂的操作比如 手动将参数转换为数组 :
function addit(theNumbers) {
var numArray =
Array.prototype.slice.call(arguments)
return numArray.reduce((prevnum, curnum) =>
prevnum + curnum, 0)
}
addit(1, 2, 3, 4)
// returns 10
Rest parameters 只能应用于函数的参数的一个子集,就像下面这样,它只会将参数从第二个开始转换为数组:
function f1(date, ...lucknumbers) {
return 'The Lucky Numbers for ' + date +
' are: ' + lucknumbers.join(', ')
}
console.log(f1('2017/09/29', 3, 32, 43, 52));
//The Lucky Numbers for 2017/09/29 are: 3, 32, 43, 52
一些小应用
1、交换变量,是一种很酷的玩法
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b) // 2 1
2、接受函数返回的多个值
function fun() {
return [1, 2, 3]
}
let [x, y, z] = fun()
console.log(x, y, z) // 1 2 3
function fun() {
return {
a: 1,
b: 2,
c: 3
}
}
let {a, b, c} = fun()
console.log(a, b, c) // 1 2 3
3、接受不按顺序的函数参数
function login({pwd, uid}) {
console.log(uid, pwd)
}
login({uid: '123', pwd: '456'}) //123 456
login({pwd: '456', uid: '123',}) //123 456
4、设置默认值
function login({pwd, uid, age = 18}) {
console.log(uid, pwd, age)
}
login({uid: '123', pwd: '456', age: 22}) //123 456 22
login({pwd: '456', uid: '123',}) //123 456 18
5、为模块化编程提供优雅的模块引入方式
import {host} from './config'
6、函数的参数列表可以自动装载成数组
function fun(head, ...others) {
console.log(head) //1
console.log(others) //[ 2, 3, 4 ]
}
fun(1, 2, 3, 4)
7、删除对象某一属性
let user = {
uid: "root",
pwd: 'pwd',
name: 'ahao'
}
let {name, ...new_user} = user
console.log(name) //ahao
console.log(new_user) //{ uid: 'root', pwd: 'pwd' }
8、合并对象(浅层赋值)
let obj1 = {
name: 'a',
age: 2,
}
let obj2 = {
name: 'b',
tel: 123
}
let newObj = {...obj1, ...obj2}
console.log(newObj) //{ name: 'b', age: 2, tel: 123 }