ES6 变量的解构赋值

最后更新:
阅读次数:

解构赋值(destructuring assignment)语法是一个 Javascript 表达式,它使得从数组或者对象中提取数据赋值给不同的变量成为可能。

// 传统写法
var a = 1;
var b = 2;
var c = 3;

// ES6 写法
var [a, b, c] = [1, 2, 3];

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面列出一些例子:

// example 1
let [a, , b] = ["aaa", "bbb", "ccc"];
console.log(`${a},${b}`); // aaa,ccc

// example 2
let [name, age, sex] = [[["percy"]], [21]];
console.log(`${name},${age},${sex}`); // percy,21,undefined

数组的解构赋值

  • 数组的解构赋值允许指定默认值
let [a = "defaultA", b = "defaultB"] = [, 2];
console.log(`${a},${b}`); // defaultA,2
  • 事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
// 对 set结构使用数组的解构赋值
let [x, y, z] = new Set(["a", "b", "c"]);
console.log(x); // a

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

var [x = 1] = [undefined];
console.log(x); // 1

var [x = 1] = [null];
console.log(x); // null

对象的解构赋值

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

// test 1
let { name, age } = { name: "percy", age: 21 };
console.log(`${name},${age}`); // percy,21

// test 2
let { name1, age1 } = { name: "percy", age: 21 };
console.log(`${name1},${age1}`); // undefined,undefined

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。看下面例子:

let { name: a, age: b } = { name: "percy", age: 21 };
console.log(`${a},${b}`); // percy,21
console.log(`${name},${age}`); // Uncaught ReferenceError: age is not defined

对对象进行解构赋值时,如果前面没有声明关键字(var、let、const),则需要对这个表达式加上圆括号。

// 不加圆括号
let name, age;
{ name, age } = { name: "percy", age: 21 }; // Uncaught SyntaxError: Unexpected token =
console.log(`${name},${age}`);

// 加圆括号
let name, age;
({ name, age } = { name: "percy", age: 21 });
console.log(`${name},${age}`); // percy,21
  • 对象的解构也可以指定默认值
var { x, y = 5 } = { x: 1 };
console.log(`${x},${y}`); // 1,5

字符串的解构赋值

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

var [a, b, c] = "percy";
console.log(`${a},${b},${c}`); // p,e,r

函数参数的解构赋值

function point([x = 0, y = 0]) {
console.log(`(${x},${y})`);
}
point([10]); // (10,0)

解构赋值的用途

  • 交换变量的值
var [x, y] = [11, 22];
console.log(`${x},${y}`); // 11,22
[x, y] = [y, x];
console.log(`${x},${y}`); // 22,11
  • 直接引用内置对象的方法
let { sin, cos, tan } = Math;
console.log(sin(1) === Math.sin(1)); // true
  • 从函数返回多个值

这个用途实际上就是从函数返回一个数组或对象,然后通过解构赋值进行提取值。

// 返回一个数组
function point() {
return [100, 50];
}
let [x, y] = point();
console.log(`${x},${y}`); // 100,50

// 返回一个对象
function person() {
return {
name: "percy",
age: 20
};
}
let { name, age } = person();
console.log(`${name},${age}`); // percy,20
  • 更灵活地为函数传参数
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);

// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
  • 更灵活地提取 JSON 数据
let jsonData = {
name: "percy",
age: 21,
friends: ["jack", "mary", "alice"]
};
let { name, age, friends } = jsonData;
console.log(`name:${name}`); // name:percy
console.log(`age:${age}`); // age:21
console.log(`friends:${friends}`); // friends:jack,mary,alice
  • 更灵活地遍历 Map 结构

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

var map = new Map();
map.set("first", "hello");
map.set("second", "world");

// 同时获取键和值
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// 只获取键
for (let [key] of map) {
console.log("key is " + key);
}
// 只获取值
for (let [, value] of map) {
console.log("value is " + value);
}
  • 输入模块的指定方法

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

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

参考资料