ES6学习笔记(二):变量的解构赋值

数组的解构赋值

在ES6中允许按照一定的模式,从数组或对象中提取值,对变量进行赋值,这种方式称为解构(Destructuring)。在ES5中,我们是这样为变量赋值的

1
2
3
var a = 1;
var b = 2;
var c = 3;

而在ES6中,我们可以这样为变量赋值

1
let [a, b, c] = [1, 2, 3];

从数组中提取值,按照对应的位置,为变量进行赋值。

本质上,这种方式属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值,同时也可以嵌套。

1
2
3
4
5
let [a, [b, c], d] = [1, [2, 3], 4]; // a=1 b=2 c=3 d=4
let [a, , c] = [1, 2, 3]; // a=1 c=3
let [a, b, c] = [1, 2]; // a=1 b=2 c=undefined
let [a, ...b] = [1, 2, 3, 4, 5]; // a=1 b=[2, 3, 4, 5]
let [a, b, ...c] = [1]; // a=1 b=undefined c=[]

如果解构不成功,变量的值就为undefined,如果等号右边不是数组(严格说是不可遍历的解构),就会报错。

1
2
3
4
5
6
7
// 以下情况都会报错
let [a] = 1;
let [a] = false;
let [a] = undefined;
let [a] = null;
let [a] = NaN;
let [a] = {};

默认值

解构赋值允许指定默认值

1
2
3
4
let [a=2] = []; // a=2
let [a, b=2] = [1]; // a=1 b=2
let [a, b=2] = [1, undefined]; // a=1 b=2
let [a, b=2] = [1, null]; // a=1 b=null (null不严格等于undefined,所以b的默认值2不生效)

注意,ES6中使用严格相等运算符(===),判断一个位置是不是有值,如果一个数组成员不是严格等于undefined,默认值就不会生效,例如上面的null不严格等于undefined,默认值就不会生效。

对象的解构赋值

解构赋值不仅可以用于数组,也可以用于对象,这个时候有两种情况:

变量与属性同名

1
2
3
let { name, age } = { name: '张三', age: 20 }; // name='张三' age=20
let { age, name } = { name: '李四', age: 30 }; // name='李四' age=30
let { name, sex } = { name: '张三' }; // name='张三' sex=undefined

变量与属性不同名

如果变量名与属性名不一样,必须写成下面这样

1
2
3
4
5
6
7
8
let { name: n, age: a } = { name: '张三', age: 20}; // n='张三' a=20
let { age: a, name: n } = { name: '张三', age: 20}; // n='张三' a=20
let { name: n, sex: s } = { name: '张三' }; // n='张三' s=undefined
// 同样也可以嵌套
let { person: {name, result}} = { person: {
name: '张三',
result: [90, 95]
}}; // name='张三' result=[90, 95]

这其实说明,对象的解构赋值是下面形式的简写

1
let { name: name, age: age } = { name: '张三', age: 20 }; // name='张三' age=20

也就是说,对象的解构赋值的内部机制,是先找到同名的属性,然后再赋值给对应的变量。真正被赋值的是后者,而不是前者。

1
2
3
let { name: n} = { name: '张三' };
console.log(n); // 张三
console.log(name); // ReferenceError: name is not defined

上面代码中,真正被赋值的是变量n而不是模式name

默认值

对象的解构赋值也可以指定默认值,默认值生效的条件是属性的值严格等于undefined

1
2
3
4
5
let { name='张三' } = {}; // name='张三'
let { name, age=30} = { name: '张三' }; // name='张三' age=30
let { name: n='张三' } = {}; // n='张三'
let { name: n='张三' } = { name: undefined }; // n='张三'
let { name: n='张三' } = { name: null }; // n=null

字符串的解构赋值

字符串也可以解构赋值,此时,字符串被转换成了一个类似数组的对象。

1
let [a, b, c, d, e] = 'hello'; // a='h' b='e' c='l' d='l' e='o'

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

1
let { length: len } = 'hello'; // len=5

函数参数的解构赋值

函数的参数也可以使用解构赋值

1
2
3
4
function add([x, y]) {
return x + y;
}
add([1, 2]); // 3

上面的代码中,函数add的参数是一个数组,但在传入参数的那一刻,数组参数就被解构成变量xy,对于函数内部代码来说,他们能感受到的参数就是xy

函数的参数也可以使用默认值

1
2
3
4
5
6
7
8
function add({x=0, y=0} = {}) {
return [x, y];
}
add({x: 3, y: 4}); // [3, 4]
add({x: 3, y: undefined}); // [3, 0]
add({x: 3}); // [3, 0]
add({}); // [0, 0]
add(); // [0, 0] 因为没有传参,使用的默认值{}

上面代码中,函数add的参数是一个对象,xy通过这个对象解构赋值,如果解构失败,xy使用默认值。

注意,下面的写法会得到不一样的结果。

1
2
3
4
5
6
7
function add({x, y} = {x: 0, y: 0}) {
return [x, y];
}
add({x: 3, y: 4}); // [3, 4]
add({x: 3}); // [3, undefined]
add({}); // [undefined, undefined]
add(); // [0, 0] 因为没有传参,使用默认值{x: 0, y: 0}

上面的代码是为函数add的参数指定默认值,而不是为变量xy指定默认值,所以会得到和上面不同的结果。

用途

交换变量的值

1
2
3
4
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1

从函数返回多个值

函数只能返回一个值,如果要返回多个值,就需要放到数组或对象中返回,有了结构赋值,取出这些返回值就简单多了。

1
2
3
4
5
6
7
8
9
function func1() {
return [1, 2, 3];
}
let [a, b, c] = func1(); // a=1 b=2 c=3
function func2() {
return {name: '张三', age: 20};
}
let {name, age} = func2(); // name='张三' age=20

函数参数的定义

解构赋值可以方便的将一组参数与变量名对应起来。

1
2
3
4
5
6
7
8
9
10
11
// 参数是有序的值
function func1([x, y, z]) {
return [x, y, z];
}
func1([1, 2, 3]); // [1, 2, 3]
// 参数是无序的值
function func2({x, y, z}) {
return {x, y, z};
}
func2({x: 1, z: 3, y: 2}); // {x: 1, y: 2, z: 3}

提取JSON数据

解构赋值提取JSON中的数据非常有用。

1
2
3
4
5
6
7
var data = {
name: '张三',
age: 90,
sex: '男',
result: [80, 90]
};
let { name, age, result } = data;

函数参数的默认值

指定参数的默认值,就避免了在函数体内再写var foo = config.foo || 'default config';这样的判断语句。

1
2
3
4
5
6
7
8
9
jQuery.ajax = function(url, {
async: true,
beforeSend: function() {},
cache: true,
complete: function() {}
// ... 更多配置
}) {
// ...
}

遍历Map结构

任何部署了Iterator接口的对象都可以使用for...of循环遍历,Map结构原生支持Iterator接口,配合解构赋值,获取键名和键值就非常方便。

1
2
3
4
5
6
7
8
var map = new Map();
map.set('name', '张三');
map.set('age', 20);
for(let [key, value] of map) {
console.log(key + ' is ' + value);
}
// name is 张三
// age is 20

输入模块的指定方法

加载模块时,往往需要指定加载模块的哪些方法,使用解构赋值使得输入非常清晰。

1
import { func1, func2, func3 } from 'module';

参考链接:
ECMAScript 6 入门(阮一峰):变量的解构赋值

坚持原创技术分享,您的支持将鼓励我继续创作!