对象和数组字面量是JavaScript中两种最常用的数据结构,由于JSON数据结构的普及,两者已经成为语言中特别重要的一部分。在编码过程中,我们经常定义许多对象和数组,然后有组织地从中提取相关的信息片段。ES6添加了可以简化这种任务的新特性:解构。
为何使用解构功能
在ES5中,开发者为了从对象和数组中获取特定数据并赋值给变量,编写了很多同质化代码。
let options={
repeat: true,
save: false
};
//从对象中提取数据
let repeat=options.repeat,
save=options.save;
这段代码中从options中提取repeat和save的值并存储的过程十分相似,如果需要得到的值很多,则会编写很多次同质化代码,如果代码中海油嵌套结构,则会非常麻烦。
所以ES6为对象和数组都添加了结构功能,将数据结构打散过称变得很简单,使我们更容易获取信息。
对象解构
对象字面量的语法形式是在一个赋值操作符左边放置一个对象字面量。
let node={
type:"Indetifier",
name:"foo"
};
let {type,name}=node;
console.log(type);//"Indetifier"
console.log(name);//"foo"
在这段代码中,node.type的值存储在名为type的变量中;node.name的值存储在名为name的变量中。
值得注意的是:如果用var、let和const解构声明变量,则必须要为其初始化,否则报错。
//语法错误
var {type,name};
//语法错误
let {type,name};
//语法错误
const {type,name};
解构赋值
我们同样可以在给变量赋值时使用解构语法。
let node={
type:"Indetifier",
name:"foo"
},
type="Literal",
name=5;
({type,name}=node);
console.log(type);//"Indetifier"
console.log(name);//"foo"
在示例中,声明type和name时初始化了一个值,在后面通过结构重新赋值。需要注意的地方是:因为开放的{}会被视为代码块,而语法规定,代码块不允许出现在赋值语句左边,所以需要加小括号使其转化为一个表达式。
结构表达式的值与表达式右侧(也就是=右侧)的值相等,如此在任何可以使用值得地方你都可以使用解构赋值表达式。
let node={
type:"Indetifier",
name:"foo"
},
type="Literal",
name=5;
function outputInfo(value){
console.log(value==node);//true
}
console.log(type);//"Indetifier"
console.log(name);//"foo"
值得注意的点是:如果右侧的值为null或undefined会导致程序抛出错误。也就是尝试读取null和undefined的属性的行为都会抛出错误。
默认值
如果指定的局部变量名称在对象中不存在,那么这个局部变量会被赋值undefined。
let node={
type:"Indetifier",
name:"foo"
};
let {type,name,value}=node;
console.log(type);//"Indetifier"
console.log(name);//"foo"
console.log(value);//"undefiined"
当指定属性不存在时,可以随意定义一个默认值。
let node={
type:"Indetifier",
name:"foo"
};
let {type,name,value=true}=node;
console.log(type);//"Indetifier"
console.log(name);//"foo"
console.log(value);//"true"
为非同名局部变量赋值
如果希望用不同命名的局部变量来存储对象属性的值,可以这样写。
let node={
type: "Identifier",
name: "foo"
};
let {type: localType,name: localName}=node;
console.log(localType);//"Identifier"
console.log(localName);//"foo"
这段代码使用了解构声明来声明变量localType和localName,这两个量分别包含node.type和node.name的值,
当使用其他变量名进行赋值时也可以为其添加默认值。
let node={
type: "Identifier",
};
let {type: localType="hahaha",name: localName="ccg"}=node;
console.log(localType);//"Identifier"
console.log(localName);//ccg
嵌套对象解构
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);//1
console.log(start.column);//1
在这个示例中,我们在解构模式中使用了花括号,其含义是在找到node对象的loc属性后,深入一层找到start属性。
更近一步,可以使用一个与对象属性名不同的局部变量名:
let node={
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
let {loc:{start: localStart}}=node;
console.log(localStart.line);//1
console.log(localStart.column);//1
对象解构功能很好用,但是值得注意的是,你可能会创建一个无效表达式,它是合法的,但是什么都干不了。
//未申明任何变量
let {loc:{}}=node;
左边的loc不是即将创建的绑定,只是代表对象中的检索属性的位置。
数组解构
与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性:
let colors=["red","green","blue"];
let [firstColor,secondColor]=colors;
console.log(firstColor);//"red"
console.log(secondColor);//"green"
在这段代码中,我们从colors数组中解构出“red”和“green”这两个值,并分别存储在变量firstColor和变量secondColor中。在数组解构语法中,我们通过值在数组的位置进行选取,且可以将其存储在任意变量中,未显式申明的元素会被忽略哦。值得注意的是,这个过程中数组不会发生任何变化。
在解构模式中,也可以直接省略元素,只为需要的元素提供变量名。
let colors=["red","green","blue"];
let [ , , thirdColor]=colors;
console.log(thirdColor);//"blue"
thiirdColor前的逗号是前方元素的占位符,无论数组元素有多少个,你都可以通过这种方式提取需要的元素,并不需要额外指定变量名。
值得注意的是:使用var、let和const声明数组解构的绑定时,必须要提供一个初始化程序。
解构赋值
数组解构也可以用于赋值上下文,但不需要用小括号包裹表达式,这一点与数组解构的约定不同。
let colors=["red","green","blue"],
firstColor="black",
secondColor="purple";
[firstColor,secondColor]=colors;
console.log(firstColor);//"red"
console.log(secondColor);//"green"
解构语法中还有一个特例:交换两个变量的值。
//在es5中交换变量
let a=1,
b=2,
tmp;
tmp=a;
a=b;
b=tmp;
console.log(a);//2
console.log(b);//1
//如果使用数组解构赋值语法,就不需要用额外的变量了。
//在ES6中交换变量
let a=1,
b=2;
[a,b]=[b,a];
console.log(a);//2
console.log(b);//1
代码执行过程中:先解构临时数组,将b和a的值复制到左侧数组的前两个位置,最终结果是变量互换了它们的值。
值得注意的是:如果右侧数组解构赋值表达式的值为null或undefined,则会导致程序 抛出错误。
默认值
也可以在数组解构赋值表达式中为数组中的任意位置添加默认值,当指定位置的属性不存在或其值为undefined时使用默认值:
let colors=["red"];
let [firstColor,secondColor="green"]=colors;
console.log(firstColor);//"red"
console.log(secondColor);//"green"
嵌套数组解构
在原有的数组模式中插入另一个数组模式,即可将解构过程深入到下一层次:
let colors=["red",["green","lightgreen"],"blue"];
//接下来
let [firstColor,[secondColor]]=colors;
console.log(firstColor);//"red"
console.log(secondColor);//"green"
不定元素
数组解构中也可以用不定元素。
let colors=["red","green","blue"];
let [firstColor,...restColors]=colors;
console.log(firstColor);//"red"
console.log(restColors.length);//2
console.log(restColors[0]);//"green"
console,log(restColors[1]);//"blue"
数组复制
//在ES5中克隆数组
var colors=["red","green","blue"];
var clonedColors=colors.concat();//concat()设计初衷是用于连接两个数组,当不传递 参数时就会返回当前的副本。
console.log(clonedColors);//
//在ES6中的新方法
let colors=["red","green","blue"];
let [...clonedColors]=colors;
console.log(clonedColors);//"[red,green,blue]"
比较这个方法和concat()方法的可读性,喜欢用哪个你就用哪个吧。QAQ...
值得注意的是在被解构的数组中,不定元素必须为最后一个条目,在后面添加逗号会报错。
混合解构
可以混合使用对象解构和数组解构来创建更多复杂的表达式。
let node={
type:"Indentifier",
name:"foo",
loc:{
start:{
line:1,
column:1
},
end:{
line:1,
column:4
}
},
range:[0,3]
};
let {
loc:{start},
range:[startIndex]
}=node;
console.log(start.line);//1
console.log(start.column);//1
console.log(startIndex);//0
解构参数
解构可以用在函数参数的传递过程中,这种特别方式更特别。当定义接受大量可选参数的JavaScript函数时,我们通常会创建一个可选对象,将额外的参数定义为这个对象的属性。
//options的属性表示其他参数
function setCookie(name,value,options){
options=options||{};
let secrue=options.secrue,
path=options.path,
domain=options.domain,
expires=options.expires;
//设置cookie的代码
}
//第三个参数映射到options中
setCookie("type","js",{
secrue:true,
expires:60000
});
这样的setCookie存在的问题是仅查看函数的声明部分,无法辨识函数的预期函数,必须阅读函数体才可以确定所有参数的情况。
定义为解构函数就可以解决这个问题。
function setCookie(name,value,{secrue,path,domain,expires}){
//设置cookie的代码
}
//第三个参数映射到options中
setCookie("type","js",{
secrue:true,
expires:60000
});
必须传值的解构参数
解构函数有个奇怪的地方,默认情况下,如果调用函数时不提供被解构的参数会导致程序抛出错误。
setCookie("type","js");//程序报错
缺失的第三个参数,其值为undefined,而解构参数只是将解构声明应用到在函数参数的一个写法,其会导致程序抛出错误。当调用setCookie()函数时,JavaScript引擎实际上做了这些事情:
function setCookie(name,value,options){
let {secure,path,domain,expires}=options;
//设置cookie的代码
}
如果解构表达式的右边为null或undefined,则程序会报错,同理,调用时不传参数也会报错。
如果希望解构函数参数可选,可以这样写:
function setCookie(name,value,{secure,path,domain,expires}={}){
//...
}
解构参数的默认值
可以为解构函数指定默认参数值,就像在解构赋值语句中一样,只需要添加默认值就好了。
function setCookie(name,value,{
secure=false,
path="/",
domain="example.com",
expires=new Date(Date.now()+360000000)
}){
//...
}
这种方法看着挺清晰,但是函数声明比以前复杂,这里建议直接用相同的解构作为默认参数:
function setCookie(name,value,{
secure=false,
path="/",
domain="example.com",
expires=new Date(Date.now()+360000000)
}={
secure=false,
path="/",
domain="example.com",
expires=new Date(Date.now()+360000000)
}
){
//...
}
现在函数变得更加完整了,第一个对象字面量是解构参数,第二个为默认值,为了解决部分代码冗余,可以将默认值封装在一个独立对象中。
const setCookDefaults={
secure=false,
path="/",
domain="example.com",
expires=new Date(Date.now()+360000000)
};
function setCookie(name,value,{
secure=false,
path="/",
domain="example.com",
expires=new Date(Date.now()+360000000)
}=setCookieDefaults
){
//...
}
在这段代码中,默认值已经被放到setCookieDefaults对象中,除了作为默认参数值,在解构参数中可以直接使用这个对象为每一个绑定设置默认值。使用解构参数后,不得不处理默认参数的复杂逻辑,但它也有好的一面,如果要改变默认值,可以立即在setCookieDefaults中修改,改变的值将自动同步到所有出现过的地方。