对象解构
let node = {
type: "Identifier",
name: "foo"
}
我们想从这个对象中提取数据,需要一项项地去解析:
let type = node.type;
let name = node.name;
但是使用解构就可以使用更简单的语法提取数据:
let {type,name} = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
对象解构语法在赋值语句的左侧使用了对象字面量,在此代码中, node.type 的值被存储到 type 本地变量中, node.name 的值则存储到 name 变量中。
此语法与对象字面量中的属性简写相同,type 与 name 标识符既声明了本地变量,也读取了对象的相应属性值。
注意!当使用解构来声明变量的时候,等号右侧必须有值,不然会抛出错误:
// 语法错误!
let { type, name };
解构赋值
上面的示例是用于变量声明的,也可以在赋值的时候使用解构:
let node = {
type: "Identifier",
name: "foo"
};
//两个本地变量
type = "Literal",
name = 5;
// 使用解构来赋值
({type,name} = node);
console.log(type); // "Identifier"
console.log(name); // "foo"
我们通过读取 node 对象来更改这type、name这两个本地变量的值。
注意!必须使用圆括号包裹结构赋值语句,因为暴露的花括号会被解析为代码块,而块语句不允许在赋值操作符(即等号)左侧出现字面量。
圆括号标示了里面的花括号并不是块语句、而应该被解释为表达式,从而允许完成赋值操作。
解构表达式的值为表达式右侧的值,也就是说({type,name} = node);
的值是node。我们可以任意使用这个值,例如传递给函数:
function outputInfo(value) {
console.log(value);
}
outputInfo({ type, name } = node);
这里node就被传给了函数。
当解构赋值表达式的右侧( = 后面的表达式)的计算结果为 null 或 undefined 时,会抛出错误。
默认值
使用解构赋值时,如果所指定的本地变量在对象中没有找到同名属性,那么该变量会被赋值为undefined。
let node = {
type: "Identifier",
name: "foo"
};
let { type, name, value } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined
由于node对象中没有value属性,因此value被赋值为undefined。
你可以选择定义一个默认值,以便指定属性不存在时使用默认值。
let {type, name, value='abc'} = node;
console.log(value); // abc
只有在node的对应属性缺失、或者对应的属性值为undefined的情况下,默认值才会被使用。
赋值给不同的本地变量名
上面的例子中都使用了对象中的属性名作为本地变量的名称。但如果想赋值给不同名的本地变量呢?
ES6允许你在给本地变量赋值时使用一个不同的名称:
let node = {
type: "Identifier",
name: "foo"
};
let {type: localType, name: localName} = node;
console.log(localType); // "Identifier"
console.log(localName); // "foo"
type: localType这种语法表示要读取名为type的属性,并赋值给变量localType。
(这种方式有点别扭啊)
也可以给变量别名添加默认值:
let node = {
type: "Identifier"
};
let {type: localType, name: localName="bar"} = node;
console.log(localType); // "Identifier"
console.log(localName); // "bar"
此处变量localName有一个默认值,由于node.name并不存在,因此其最终被赋予了默认值。
嵌套的对象解构
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属性。
每当有一个冒号在解构模式中出现,就意味着冒号之前的标识符代表需要检查的位置。
在刚刚为不同的本地变量名赋值中也是,type: localType
就表示要在type中寻找。此处则是要在loc中寻找。
当冒号右侧存在花括号时,表示目标被嵌套在对象的更深一层中。
此时就代表我们要在loc内部寻找start。
也可以在嵌套解构中为本地变量使用不同的名称:
let {loc:{start: localStart}} = node;
console.log(localStart.line); // 1
console.log(localStart.column); // 1
在此版本的代码中, node.loc.start 的值被存储在一个新的本地变量 localStart 上.
解构模式可以被嵌套在任意深度的层级,并且在每个层级的功能都一样。
数组解构
数组解构的语法看起来与对象解构非常相似,只是将对象字面量替换成了数组字面量,解构作用在数组内部的位置上,而不是作用在对象的具名属性上。
let colors = [ "red", "green", "blue" ];
let [firstColor, secondColor] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
此处数组解构从 colors 数组中取出了 “red” 与 “green” ,并将它们赋值给 fristColor 与 secondColor 变量。
这些值是根据数据中的位置取出的,firstColor对应第一个值,secondColor对应第二个值,而变量名称和取值是无关的。(与对象解构不同)
任何没有再解构模式中明确指定的项都会被忽略,但数组本身并没有任何改变。
你也可以在解构模式中忽略一些项,只给想提取的项提供变量名。
例如,只想取得数组中的第三个元素,那么不必给前两项提供变量名:
let [ , , thirdColor] = colors;
console.log(thirdColor); // "blue"
模式中 thirdColor 之前的逗号,是为数组前面的项提供的占位符。使用这种方法,你就可以轻易从数组任意位置取出值,而无须给其他项提供变量名。
解构赋值
同样,不光是可以声明变量,也可以为现有变量赋值:
let colors = [ "red", "green", "blue" ],
firstColor = "black",
secondColor = "purple";
[ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
数组解构有一个非常独特的用例,就是可以交换两个变量的值。
一般的做法是引入第三个变量作为临时变量,但是使用数组解构就不需要:
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"
此代码的 colors 数组只有一个项,因此没有能与 secondColor 匹配的项,又由于此处有个默认值, secondColor 的值就被设置为 “green” ,而不是 undefined 。
嵌套解构
与解构嵌套的对象相似,可以用类似的方式来解构嵌套的数组。在整个解构模式中插入另一个数组模式,解构操作就会下行到嵌套的数组中:
let colors = [ "red", [ "green", "lightgreen" ], "blue" ];
let [firstColor,[secondColor]] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
secondColor在第二个方括号中,因此就要到第二层数组中取值。
与对象解构相似,你也能使用任意深度的数组嵌套。
剩余项
ES6的函数存在剩余参数的概念,指的是 …args可以将剩下的未明确声明的参数囊括。
而数组解构也有剩余项的概念,它使用 … 语法来将剩余的项目赋值给一个指定的变量。
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"
第一个项赋给了firstColor,剩余的项则赋给了一个新的restColors数组。
若要取出特定项并要求保留剩余的值,则剩余项是非常有用的。
剩余项还可以用于克隆数组。
在ES5中一般使用concat来克隆数组:
var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();
console.log(clonedColors); //"[red,green,blue]"
不给concat传任何参数,就会获得原数组的一个克隆。
而现在可以使用剩余项的语法达到相同的目的:
var [...clonedColors] = colors;
和数组解构语法相同,使用剩余项使得colors的所有值都赋给了clonedColors数组。
剩余项必须是数组解构模式中最后的部分,之后不能再有逗号,否则就是语法错误。
混合解构
对象与数组解构能被用在一起,以创建更复杂的解构表达式。在对象与数组混合而成的结构中,这么做便能准确提取其中你想要的信息片段。
let node = {
type: "Identifier",
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
此处loc与range只是对应于node对象中属性的位置。
混合使用对象与数组解构, node 的任何部分都能提取出来。对于从 JOSN 配置结构中抽取数据来说,这种方法尤其有用,因为它不需要探索整个结构。
参数解构
在传递函数参数时,当函数接受大量可选参数时,一个常用模式是创建一个 options 对象,其中包含了附加的参数:
function setCookie(name, value, options) {
options = options || {};
let secure = options.secure,
path = options.path,
domain = options.domain,
expires = options.expires;
}
此处,name与value属性是必须的,而其他参数则不是,通过一个options选项统一传入。
参数解构提供了更清楚地标明函数期望输入的替代方案。它使用对象或数组解构的模式替代了具名参数:
function setCookie(name,value,{secure,path,domain,expire}){
……
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
第三个参数使用了解构来解析必要的数据。此时可选项目存在于额外的参数组中,若传递了则可以直接获取值,若没有传递则为undefined。
但是这种方式要求参数解构一定有值,因为js引擎实际上是这样做的:
function setCookie(name, value, options) {
let { secure, path, domain, expires } = options;
}
因此如果options没有传,那么就相当于解构赋值的右侧为null,那么肯定会报错:
// 出错!
setCookie("type", "js");
因此一个比较好的实践是给解构的参数提供默认值:
function setCookie(name, value, {secure, path, domain, expires } = {}){
……
}
此例为第三个参数提供了一个空对象作为其默认值。给解构的参数提供默认值,也就意味着若未向 setCookie() 函数传递第三个参数,则 secure 、 path 、 domain 与 expires 的值全都会是 undefined ,此时不会有错误被抛出。
参数解构的默认值
你可以为参数解构提供可解构的默认值,就像在解构赋值时所做的那样,只需在其中每个参数后面添加等号并指定默认值即可。例如:
function setCookie(name, value,
{
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000)
} = {}
) {
// ...
}
此代码中参数解构给每个属性都提供了默认值,所以你可以避免检查指定属性是否已被传入(以便在未传入时使用正确的值)。而整个解构的参数同样有一个默认值,即一个空对象,令该参数成为可选参数。这么做使得函数声明看起来比平时要复杂一些,但却是为了确保每个参数都有可用的值而付出的微小代价。
总结
解构使得在 JS 中操作对象与数组变得更容易。使用熟悉的对象字面量与数组字面量语法,可以将数据结构分离并只获取你感兴趣的信息。对象解构模式允许你从对象中进行提取,而数组模式则能用于数组。
对象与数组解构都能在属性或项未定义时为其提供默认值
在赋值表达式右侧的值为 null 或 undefined 时,两种模式都会抛出错误。
你也可以在深层嵌套的数据结构中使用对象与数组解构,下行到该结构的任意深度。