ES6之解构

为何使用解构功能

在编码过程中,我们经常定义许多对象和数组,然后有组织地从中提取相关的信息片段;如果对象跟数组结构复杂,我们就需要深入挖掘整个数据结构才能找到所需数据。

所以ES6位对象与数组都添加了结构功能,将数据结构打散的过程变得更加简单;许多语言都通过极少量的语法实现了结构功能,以简化获取信息的过程;而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"

使用var let或const解构声明变量,必须要提供初始化程序。下面几行代码全部都会导致抛出语法错误,它们都缺少了初始化程序:

1
2
3
4
5
6
// syntax error!
var { type, name };
// syntax error!
let { type, name };
// syntax error!
const { type, name };

解构赋值

如果在定义变量之后想要修改它们的值,需要用小括号包裹解构赋值语句,因为JS引擎将一对开放的花括号视为一个代码块,而语法规定,代码块语句不允许出现在赋值语句左侧,添加小括号后可以将块语句转化为一个表达式,从而实现解构赋值的过程。如下面的({ type, name } = node)语句:

1
2
3
4
5
6
7
8
9
10
let node = {
type: "Identifier",
name: "foo"
},
type = "Literal",
name = 5;
// assign different values using destructuring
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name);

解构赋值表达式(=号右侧)如果为null或者undefined会导致程序抛出错误;let {hello} = null会抛出错误Cannot destructure propertyhelloof 'undefined' or 'null'.

默认值

当指定的局部变量名称在对象中不存在的时候,会被赋值为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
8
let node = {
type: "Identifier",
name: "foo"
};
let { type: localType, name: localName, first_name: firstName='hi' } = node;
console.log(localType); // "Identifier"
console.log(localName); // "foo"
console.log(firstName); // "hi"

嵌套解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
3
4
let colors = [ "red", "green", "blue" ];
let [ firstColor, ,thirdColor ] = colors;
console.log(firstColor); // "red"
console.log(thirdColor); // "blue"

当通过var let或者const声明数组解构的绑定时,必须要提供一个初始化程序;

解构赋值

数组解构也可用于赋值上下文,不需要用小括号包裹, 因为[]跟{}不一样,后者有块级的功能;

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
let a = 1,
b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1

与对象解构一样,如果右侧为null或者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" ];
// later
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"

通过上面这个语法,可以用来实现生成数组的副本;

1
2
3
4
// cloning an array in ECMAScript 6
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors); // "[red,green,blue]"

被解构的数组中,不定元素必须为最后一个条目,在后面继续添加逗号会导致程序抛出语法错误

混合解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

解构参数

ES5解析参数可能会这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// properties on options represent additional parameters
function setCookie(name, value, options) {
options = options || {};
let secure = options.secure,
path = options.path,
domain = options.domain,
expires = options.expires;
// code to set the cookie
}
// third argument maps to options
setCookie("type", "js", {
secure: true,
expires: 60000
});

利用解构我们可以:

1
2
3
function setCookie(name, value, { secure, path, domain, expires }) {
// code to set the cookie
}

必须传值的解构参数

解构参数在默认情况下,如果调用函数时不提供被解构的参数会导致程序抛出错误;如果这样调用上面的setCookie函数setCookie('type', 'js')会抛出错误;

实际上JS是这样解析的:

1
2
3
4
function setCookie(name, value, options) {
let { secure, path, domain, expires } = options;
// code to set the cookie
}

当第三个参数不传值时,实际options被赋值为undefined,根据前面讨论过的解构赋值表达式=号右边的值不能为undefined或者null,所以第三个参数是必须传值的;

可以设置默认值来避免必须传值:

1
2
3
function setCookie(name, value, { secure, path, domain, expires } = {}) {
// empty
}

解构参数默认值

1
2
3
4
5
6
7
8
9
function setCookie(name, value,
{
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000)
} = {} ){
// empty
}

也可以为第三个参数设置一个默认对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const cookieDefault = {
secure: false,
path: '/',
domain: 'example.com',
expires: new Date(Date.now() + 360000000)
};
function setCookie(name, value,
{
secure = cookieDefault.secure,
path = cookieDefault.path,
domain = cookieDefault.domain,
expires = cookieDefault.expires
} = cookieDefault ){
// empty
}