1. 数组的解构赋值
在ES5中,为变量赋值必须采用直接赋值的形式
var foo = 'abc'var bar = 123复制代码
利用ES6的解构赋值,可以简化为
let [foo, bar] = ['abc', 123]复制代码
上面的代码表示,声明foo和bar两个变量,并且按照两边数组中的顺序一一初始化值,实际上解构赋值的写法属于一种“模式匹配”,这种“模式匹配”还可以用于更复杂的数据解构中。
let [foo, [[bar, baz], qux]] = ['147', [['258', '369'], 'abc']]foo // '147' bar // '258'baz // '369' qux // 'abc'let [, , foo] = ['147', '258', '369']foo // '369'const [foo, bar, ..., qux ] = ['a', 'b', 'c', 'd', 'e'];foo // 'a'bar // 'b'qux // 'e'let [foo, [bar, ...baz]] = ['a', ['b', 'c', 'd', 'e']]foo // 'a'bar // 'b'baz // ['c', 'd', 'e']let [x, y, ...z] = ['a'];x // "a"y // undefinedz // []复制代码
如果赋值方无法满足被赋值方的结构, 则部分变量解构不成功,解构不成功的变量等于undefined, 以下情况baz解构不成功, 值为undefined。赋值方的值,有且只有undefined无法使解构赋值失败/默认值生效
var [baz] = [];var [qux, baz] = [1];复制代码
如果赋值方能满足被赋值方的结构, 而且还有多余,则能匹配的部分就解构成功
let [foo, [bar], qux] = [1, [2, 3], 4]foo // 1bar // 2qux // 4复制代码
如果赋值方是不可遍历的结构, 则解构会报错
// 报错let [qux] = 1;let [qux] = false;let [qux] = undefined;复制代码
对于Set结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(["a", "b", "c"]);x // "a"复制代码
事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。 下面的代码中fibs是一个Generator函数,原生带有iterator接口, 解构赋值依次从iterator 接口取值
function* fibs() { var a = 0; var b = 1; while (true) { yield a; [a, b] = [b, a + b]; }}var [first, second, third, fourth, fifth, sixth] = fibs();sixth // 5复制代码
解构赋值允许指定默认值
[x, y = 'b'] = ['a']; // x='a', y='b'[x, y = 'b'] = ['a', null]; // x='a', y=null[x, y = 'b'] = ['a', undefined]; // x='a', y='b'复制代码
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。下面代码中,因为x能取到值,所以函数f根本不会执行。
function f() { console.log('aaa');}let [x = f()] = [1];复制代码
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = [2]; // x=2; y=2let [x = 1, y = x] = [1, 2]; // x=1; y=2let [x = y, y = 1] = []; // ReferenceError复制代码
2. 对象的解构赋值
简单的对象解构赋值:
const { foo, bar } = { foo: "a", bar: "b" };foo // "a"bar // "b"复制代码
数组的解构赋值是依据按数组元素的顺序依次解构的,不同的是,对象的解构赋值对属性的顺序没有要求,而是需要属性名相对应才能成功解构赋值。
const { bar, foo } = { foo: "aaa", bar: "bbb" };foo // "aaa"bar // "bbb"const { qux } = { foo: "aaa", bar: "bbb" };qux // undefined复制代码
上面的第二个例子,在赋值方找不到属性名qux,被赋值方的取不到值,为undefined
如果变量名与属性名不一致,必须写成下面这样。
let obj = { first: 'hello', last: 'world' };let { first: f, last: l } = obj;f // 'hello'l // 'world'复制代码
回顾对象解构赋值的第一个例子,其写法实际是以下写法的简写。
const {foo: foo, bar: bar} = {foo: "a", bar: "b"}复制代码
下面的代码中,先用let声明变量,再在解构赋值中参与赋值,注意行首的圆括号是必须的,因为以大括号起首的语句会被执行器认为是匿名代码块,而不是解构赋值,所以需要用圆括号表示其意义
let foo;({foo} = {foo: 1}); // 成功let baz;{bar: baz} = {bar: 1}; // 报错复制代码
解构赋值用于对象和数组嵌套的解构, 注意k和p是属性名,只有a和b才是被赋值的变量
const foo = { k: [ '123', { p: '456' } ]}const {k: [a, {p: b}]} = foo;a // '123'b // '456'k // error: k is undefinedp // error: p is undefined复制代码
把被赋值方的值解构赋值到对象/数组中
const o = {}, a = [];({name: o.name, age: a[0]} = {name: 'Miku', age: 10})o // {name: 'Miku'}a // [10]复制代码
对象的解构赋值也可以指定默认值
var {x = 3} = {};x // 3var {x, y = 5} = {x: 1};x // 1y // 5复制代码
和数组的解构赋值一样,被赋值方有且只有undefined可以使解构赋值失败/默认值生效
var {x = 3} = {x: undefined};x // 3var {x = 3} = {x: null};x // null复制代码
对象的解构赋值,可以很方便地将现有对象的值(包括简单类型,引用类型,函数等),赋值到某个变量。
const {promise1, promise2} = fn()Promise.all(promise1, promise2).then(...)复制代码
3. 函数参数的解构赋值
函数的参数也可以使用解构赋值
function add([x, y]){ return x + y;}add([1, 2]); // 3复制代码
函数参数的解构也可以使用默认值。
function move({x = 0, y = 0} = {}) { return [x, y];}move({x: 3, y: 8}); // [3, 8]move({x: 3}); // [3, 0]move({}); // [0, 0]move(); // [0, 0]复制代码
和对象的解构赋值一样,函数参数的赋值方有且只有undefined能使解构失败/默认值生效
[1, undefined, 3].map((x = 'yes') => x);// [ 1, 'yes', 3 ]复制代码
4. 其他类型的解构赋值
解构赋值的规则是,只要赋值方的值不是对象,就先将其转为对象。
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';a // "h"b // "e"c // "l"d // "l"e // "o"复制代码
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';len // 5复制代码
数值和布尔值解构赋值时,会先转为对象。
let {toString: s} = 123;s === Number.prototype.toString // truelet {toString: s} = true;s === Boolean.prototype.toString // true复制代码
由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined; // TypeErrorlet { prop: y } = null; // TypeError复制代码
5. 应用场景
交换变量的值,不再需要创建中间值
[foo, bar] = [bar, foo];复制代码
分别获取函数返回的多个值
function fn () { return {foo: '123', bar: '234'}}const {foo, bar} = fn()复制代码
函数接收参数时直接解构
function fn ({foo, bar, qux}) { return `${foo} ${bar} ${qux}!`}const param = { foo: 'Welcome', bar: 'to', qux: 'javascript'}console.log(fn(param))//Welcome to javascript!复制代码
提取json中的值作为变量
const person = { name: 'Miku', age: 1, birth: '1996-2-18'}let {name, age, birth} = person;console.log(name, age, birth)//Miku 1 1996-2-18复制代码
遍历ES6 Map解构, 如果只想把key赋值给变量, 可以写成[key], 如果指向把value赋值给变量,则写[, value]
const areas = new Map()areas.set('浙江省', '杭州市')areas.set('广东省', '广州市')for (let [key, value] of areas) { console.log(`${key}-${value}`);}//浙江省-杭州市//广东省-广州市复制代码
node module.exports/require函数和ES6 export/import
//node module.exports/requireconst person = {name: 'Miku', age: 1}const student = {grade: 3, class: 2}module.exports = {person, student}const {person} = require('......')console.log(person)//{name: 'Miku', age: 1}//ES6 export/importexport const fnCollect = { fn1 () { return 'this is fn1' }, fn2 () { return 'this is fn2' }}//importimport {fn1} from '......'console.log(fn1())//this is fn1复制代码