简介
结构是一种打破数据结构,将其分拆为更小部分的过程。它可以方便我们有组织地从对象或数组中提取感兴趣的信息片段,是 ES6 新增加的特性。
对象解构
将对象解构用于变量声明的用法如下:
1 2 3 4 5 6 7
| let node = { type: "Identifier", name: "foo" }; let { type, name } = node; console.log(type); // "Identifier" console.log(name); // "foo"
|
变量赋值使用解构语法:
1 2 3 4 5 6 7 8 9 10
| let node = { type: "Identifier", name: "foo" }, type = "Literal", name = 5; // 使用解构来分配不同的值 ({ type, name } = node); console.log(type); // "Identifier" console.log(name); // "foo"
|
注意,一定要用小括号包裹结构赋值语句。因为 JS 引擎将一对开放的花括号视为一个代码块,而代码块不允许出现在赋值语句左侧,而添加小括号后可以将块语句转化为一个表达式,从而实现解构赋值。
另外,解构表达式的值与表达式右侧,也就是 "=" 右侧的值相等
。比如:
1 2 3 4
| function outputInfo(value) { console.log(value === node); // true } outputInfo({ type, name } = node);
|
上面的代码等于将 node 作为实参传入了 outputInfo 接口。
但如果结构表达式的值为 null 或 undefined 时,会抛出错误。也就是说,任何尝试读取 null 或 undefined 属性的行为都会触发运行时错误
。
使用解构时,如果指定的局部变量名称在对象中不存在,这个局部变量就会被赋值成 undefined:
1 2 3 4 5 6 7 8
| let node = { type: "Identifier", name: "foo" }; let { type, name, value } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // undefined
|
而当指定的属性不存在时,可以通过如下方式定义默认值:
1 2 3 4 5 6 7 8
| let node = { type: "Identifier", name: "foo" }; let { type, name, value = true } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // true
|
如果想使用不同命名的局部变量来存储对象属性的值,可以通过下面的扩展语法满足需求:
1 2 3 4 5 6 7
| let node = { type: "Identifier", name: "foo" }; let { type: localType, name: localName } = node; console.log(localType); // "Identifier" console.log(localName); // "foo"
|
其中 type 和 name 是对象中的属性,localType 和 localName 是想要新赋值的变量名。
当然,使用其它变量名赋值时,也可以添加默认值:
1 2 3 4 5 6
| let node = { type: "Identifier" }; let { type: localType, name: localName = "bar" } = node; console.log(localType); // "Identifier" console.log(localName); // "bar"
|
可以通过嵌套对象解构来从一个对象的内嵌对象中获取数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
|
用下面方式解构,会抛出异常:
1 2
| const node = {}; const { loc: { name } } = node;
|
因为 node 对象中没有 loc,因此 loc 的值是 undefined;此时再从 loc 中解构 name 就会抛出下面的异常:
1
| Uncaught TypeError: Cannot read property 'value' of undefined
|
为了解决这种问题,可以结合前面说过的解构默认值来方便地编码同时不会抛异常:
1 2
| const node = {}; const { loc: { name } = {} } = node;
|
此时如果 node 中没有 loc 属性,loc 的默认值就会是空对象,从空对象中解构出的 name 值是 undefined。当然同样的道理,也可以给 name 指定一个默认值。
数组解构
与对象解构相比,数组结构就简单多了:
1 2 3 4
| let colors = [ "red", "green", "blue" ]; let [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
|
在数组解构语法中,我们可以通过值在数组中的位置进行选取,未显式声明的元素都将被忽略,比如上例中的 “blue”。
也可以直接省略元素,只为感兴趣的元素提供变量名:
1 2 3
| let colors = [ "red", "green", "blue" ]; let [ , , thirdColor ] = colors; console.log(thirdColor); // "blue"
|
数组解构也可以用于变量赋值,只是因为没有大括号代码块,所以不需要用小括号包裹表达式:
1 2 3 4 5 6
| let colors = [ "red", "green", "blue" ], firstColor = "black", secondColor = "purple"; [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
|
变量交换是数组解构的独特用法,可以让我们不需要定义额外的中间变量就能实现互换两个变量的值:
1 2 3 4 5 6
| // 在 ES6 中互换值 let a = 1, b = 2; [ a, b ] = [ b, a ]; console.log(a); // 2 console.log(b); // 1
|
也可以在数组解构表达式中为数组中的任意位置变量添加默认值,当对应位置的变量不存在或值为 undefined 时使用默认值:
1 2 3 4
| let colors = [ "red" ]; let [ firstColor, secondColor = "green" ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
|
与嵌套对象解构语法类似:
1 2 3 4 5
| let colors = [ "red", [ "green", "lightgreen" ], "blue" ]; // 随后 let [ firstColor, [ secondColor ] ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
|
可以通过 ...
语法将数组中的其余元素赋值给一个特定变量:
1 2 3 4 5 6
| 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 中,开发者常用 contact()
方法来克隆数组:
1 2 3 4
| // 在 ES5 中克隆数组 var colors = [ "red", "green", "blue" ]; var clonedColors = colors.concat(); console.log(clonedColors); //"[red,green,blue]"
|
contact()
方法的设计初衷是连接两个数组,但如果调用时不传递参数就会返回当前数组的拷贝。
在 ES6 中可以通过不定元素的语法来实现相同的目标:
1 2 3 4
| // 在 ES6 中克隆数组 let colors = [ "red", "green", "blue" ]; let [ ...clonedColors ] = colors; console.log(clonedColors); //"[red,green,blue]"
|
注意,在被解构数组中,不定元素必须为最后一个条目,如果在后面继续添加逗号会抛出语法错误。
字符串解构
可以将字符串解构成数组对象:
1 2 3 4 5 6
| const [a, b, c, d, e] = 'hello'; console.log(a);//"h" console.log(b);//"e" console.log(c);//"l" console.log(d);//"l" console.log(e);//"o"
|
类似数组的对象都有一个 length 属性,因此还可以像普通对象解构一样对这个属性解构赋值:
1 2
| const {length} = 'hello'; console.log(length); // 5
|
数值和布尔值解构
解构赋值的规则是,只要等号右侧的值不是数组或对象,就先将其转换为对象。因此数值和布尔值解构时会转为对象:
1 2 3 4
| let {toString:s1} = 123; console.log(s1 === Number.prototype.toString);//true let {toString:s2} = true; console.log(s2 === Boolean.prototype.toString);//true
|
下面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式):
1 2 3 4 5 6 7
| // 报错 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {};
|
事实上,只要某种数据结构 具有 Iterator 接口
,都可以采用数组形式的解构赋值。
函数参数
如果一个方法的入参是个对象,外部传参的时候不容易看出对象包含哪些属性,则可以通过下面的方式声明方法形参:
1 2 3 4 5 6 7
| function setCookie({ secure, path, domain, expires }) { // 设置 cookie 的代码 } setCookie({ secure: true, expires: 60000 });
|
可以通过下面的方式为解构参数设置默认值:
1 2 3
| function setCookie({ secure= true, path, domain, expires }) { // 设置 cookie 的代码 }
|
参考文档