ES6-变量的解构赋值

1.数组的结构赋值

基本用法
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。

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

这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会呗赋予对应的值。如果解构不成功,变量的值就等于undefined。
对于Set结构,也可以用数组的解构赋值

1
let [x, y, z] = new Set(['a', 'b', 'c']);

默认值
解构赋值允许指定默认值

1
2
var [foo = true] = [];
foo; // true

ES6内部使用严格相等运算符(===),判断一个位置是否有值。如果一个数组成员不严格相等于undefined,默认值则不会生效。

1
2
3
4
5
var [x = 1] = [undefined];  // 匹配到数组内的第一个成员是undefined,所以默认值生效
x; // 1

var [x = 1] = [null]; // 匹配到的值生效了,默认值就不生效了。
x; // null

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

1
2
3
4
5
6
function f(){
return 'aaa';
}
let [x = f()] = [1];
x; // 1;
// 因为x能取到值,所以函数f根本不会执行。

2.对象的解构赋值

对象的结构和数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

1
2
3
4
5
6
var { bar, foo} = { foo: 'aaa', bar: 'bbb' };
foo; // aaa;
bar; // bbb;

var { baz } = { foo: 'aaa', bar: 'bbb' };
baz; // undefined;

实际上,对象的解构赋值是下面这种:
先找到同名属性,然后再赋给对应的变量。真正被赋值的是变量foo(后者),而不是模式foo(前者)。

1
var { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };

对于letconst来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。

1
2
let foo;
let {foo} = {foo: 1}; // Duplicate declaration 'foo'

解构嵌套结构的对象

1
2
3
4
5
6
7
8
9
var obj = {
p: [
'hello',
{y: 'world'}
]
}
var { p: [x, {y}]} = obj; // 此时的p是模式,不是变量。
x; // hello
y; // world

对象的解构也可以指定默认值,默认值生效的条件是,对象的属性值严格等于undefined。如果解构失败,变量的值等于undefined
如果要将一个已经声明的变量用于解构赋值,需要小心;因为JavaScript引擎将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。

1
2
3
4
5
6
// 错误写法
var x;
{x} = {x: 1};

// 正确写法
({x} = {x:1});

由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

1
2
3
4
var arr = [1, 2, 3];
var {0: first, [arr.length -1]: last} = arr;
first; // 1;
last; // 3;

3.字符串的解构赋值

字符串也可以解构赋值,这是因为此时,字符串被转换成了一个类似数组的对象。类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

1
2
3
4
5
6
7
8
const [a, b, c, d] = 'hello';
a; // h
b; // e
c; // l
d; // l

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

4.数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

1
2
3
4
5
let {toString:s} = 123;
s === Number.prototype.toString; // true

let {toString:s} = true;
s === Boolean.prototype.toString; // true

解构赋值的规则是:只有等号右边的值不是对象,就先将其转换为对象。

5.函数参数的解构赋值

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

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

上述代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。
函数参数的解构也可以使用默认值

1
2
3
4
5
function move({x=0, y=0} = {}){
return [x, y];
}
move({x:3, y:8}); // [3,8]
move(); // [0,0]

undefined就会触发函数参数的默认值

1
2
[1, undefined, 3].map((x='yes') => x);
// [1, 'yes', 3]

6.圆括号

不能使用圆括号的情况

  • 变量声明语句中,不能带有圆括号
  • 函数参数中,模式不能带有圆括号
  • 赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号中。

7.用途

1.交换变量的值

1
[x, y] = [y, x];

2.从函数返回多个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 返回一个数组
function example(){
return [1,2,3];
}
var [a,b,c] = example();

// 返回一个对象
function example(){
return {
foo: 1,
bar: 2
}
}
var {foo, bar} = example();

3.函数参数的定义

1
2
3
4
5
6
7
// 参数是一组有次序的值
function f([x, y, z]){...}
f([1,2,3]);

// 参数是一组无次序的值
function f({x, y, z}){...}
f({z:3, y:2, x:1});

4.提取JSON数据
解构赋值对提取JSON对象中的数据尤其有用。

1
2
3
4
5
6
7
var jsonData = {
id: 25,
state: 'ok',
data: [123, 456]
};
let {id, state, data:number } = jsonData;
console.log(id, state, number); // 25, 'ok', [123, 456]

5.函数参数的默认值

1
2
3
4
5
6
7
8
9
10
11
jQuery.ajax = function(url, {
async = true,
beforeSend = function(){},
cache = true,
complete = function(){},
crossDomain = false,
global = true
// more config
}){
// do stuff
}

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

1
2
3
4
5
6
7
8
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for(let [key,value] of map) {
console.log(key + ' is ' + value);
}
// first is hello
// second is world

7.输入模块的指定方法

1
const {SourceMapConsumer, SourceNode } = require('source-map');