解构: ES6
中容许按照必定的模式从数组和对象中提取值,而后对变量进行赋值,这被称为解构(Destructuring).数组
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予相应的值。数据结构
let [a, b, c] = [1, 2, 3]; console.log(a); // 输出1 let [a, b] = [1]; console.log(b); // 解构不成功时返回undefined
不彻底解构app
等号左边只匹配到等号右边的一部分。函数
let [a, [b], c] = [1, [2, 3], 4]; console.log(b); // 输出2,只匹配到右边的右边数组[2, 3]中的2
不能解构状况ui
若是等号右边不是可遍历的解构,或者说等号右边的值或是转换为对象之后也不具有 Iterator
接口,那么就会解构失败。prototype
let [a] = 1; let [a] = false; let [a] = NaN; let [a] = undefined; let [a] = null; let [a] = {}
总结:事实上,只要某种数据结构具备 Iterator
接口,均可以采用数组形式的解构赋值。code
解构赋值容许指定默认值。对象
ES6内部使用严格相等运算符(===)判断数组的某个位置是否有值。因此,若是一个数组成员不严格等于undefined,默认值是不会生效的。接口
let [a = 2] = [null]; // a = null let [a = 2] = []; // a = 2
若是默认值是一个表达式,那么这个表达式是惰性求值的字符串
function f() { return 2; } let [a = f()] = [1]; // a = 1, f函数不会执行 let [a = f()] = []; // a = 2, f函数会执行
默认值能够引用解构赋值的其它变量,可是该变量必须已经声明
let [x = 1, y = x] = []; // x = 1, y = 1 let [x = y, y = 1] = []; // ReferenceError,在使用变量y时还并无声明
对象解构赋值的内部机制是先找到同名属性,而后再赋值给对应的变量,真正被赋值的是后者而不是前者
举例说明:
let {foo:bar} = {foo:"aaa"}; console.log(bar); // bar = "aaa" console.log(foo); // ReferenceError, foo is not defined console.log({foo:bar}.foo); // {foo:bar}.foo = "aaa"
上面代码中 foo
是匹配的模式,经过 foo
匹配到对象 {foo:bar}
中 foo
属性的值( bar
变量),而后将值赋给 bar
变量,这样对象 {foo:bar}
中 foo
属性就有值了,即 bar
的值 "aaa"
.
// 实际上 let {foo1, foo2} = {foo1:"aaa", foo2:"bbb"}; // 是下面语句的简写形式 let {foo1:a, foo2:b} = {foo1:"aaa", foo2:"bbb"};
对象的解构也能够指定默认值,默认值生效的条件是,对象的属性值严格等于undefined
let {x = 3} = {x: undefined}; // x = 3 // 上面的语句等价于 let {x:x = 3} = {x: undefined}; // x = 3 // {x:x = 3} = 3 let {x = 3} = {x: null}; // x = null
若是解构模式是嵌套的对象,并且子对象所在的父属性不存在,那么将会报错
let {foo: {bar}} = {bar: 'bar'}; // 报错,由于foo = {bar} = undefined,{bar}对象中的bar属性在解构时会报错,由于{bar}是undefined,undefined是不能转换为对象的,对undefined取bar属性会报错。 // 和下面的代码原理同样: let obj = {bar: "bar"}; obj.foo.bar // 报错,由于obj.foo = undefined,对undefined取属性会报错。 "bar".foo // undefined,不会报错,由于字符串能够转换为对象
在将已经声明的变量进行解构赋值时,要注意解构赋值语句不能直接写在行首
let x; {x} = {x: 1}; // 报错,这个地方不是很懂 // 书上的解释是:JS引擎将{x}理解成一个代码块,从而发生语法错误。 // 若想避免这个错误,能够这样写: ({x} = {x: 1});
字符串能够解构赋值是由于字符串能够转换成一个相似数组的包装对象。
let [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; console.log(s === Number.prototype.toString) // true
数值 123
被转换为对象,数值对象中有 toString
方法,与 toString
匹配,变量 s
中存储的是 toString
方法,该方法就是 Number
原型对象中的 toString
方法。
函数参数的解构也可使用默认值
function f({x = 0, y = 0} = {}) { return {x, y}; } console.log(f({x: undefined, y: 1})); // 输出: {0, 1}
详细过程:调用函数f后,{x = 0, y = 0}={x:undefined,y:1}
因为x是undefined,故x使用默认值即x=0,因为y:1故经过解构赋值后y=1.
以前的误区:注意是解构赋值,并非对象之间的赋值。
再看下面这个例子:
function f({x, y} = {x: 0, y: 0}) { return {x, y}; } console.log(f({x: undefined, y: 1})); // 输出: {undefined, 1} // 调用函数后,{x,y}={x:udefined,y:1},解构赋值后x=undefined,y=1 console.log(f({x: 1, y: 1})); // 输出: {1, 1} // 调用函数后,{x,y}={x:1,y:1},解构赋值后x=1,y=1 console.log(f({})); // 输出: {undefined, undefined} // 调用函数后,{x,y}={}={undefined,undefined},解构赋值后x=undefined,y=undefined console.log(f()); // 输出: {0, 0} 当不传递实参时,形参的值是原有的值 // 调用函数后,{x,y}={x:0,y:0},解构赋值后x=0,y=0
再次重申一下 {x,y}={x:1,y:2}
这种形式是解构赋值,千万不要理解成对象之间的赋值!
对于编译器而言,一个式子究竟是模式仍是表达式,没有办法一开始就知道,必须解析到(或解析不到)等号才能知道
不能使用圆括号的状况
可使用圆括号的状况
赋值语句中的非模式部分可使用圆括号。
[(a)] = [3]; // a并非模式 ({ p: (d) } = {}); // p是模式,d并非模式
建议:不管什么状况都尽可能不要在模式中使用圆括号
交换变量的值
let a = 1; let b = 2; [a, b] = [b, a];
方便处理函数返回值
function f([x, y]) { return [x+1, y+2]; } let [a, b] = f([1, 1]);
函数参数定义
解构赋值能够方便地将一组参数与变量名对应起来。
function f1({x, y, z}){ return x+y+z; } f1({y: 1, z: 2, x: 3 }); // 能够作到实参没有次序
提取JSON数据
能够很方便地从JSON中提取须要的数据。
let JSON = { name: "happyCoding1024", age: 18, hobby: "coding" } let [name, age, hobby] = JSON;
函数参数默认值
很是简化方便地使用函数参数默认值。
function f([x=0,y=0] = []){ return x+y; } f([]); // 当传入的是undefined时,就会使用默认值
遍历Map结构
任何部署了 Iterator
接口的对象均可以用 for...of
循环遍历。Map
结构原生支持 Iterator
接口,配合变量的解构赋值获取键名和键值很是方便。
let map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] = map){ console.log(key + "is" + value); }
输入模块的指定方法
加载模块时,每每须要制定输入的方法,解构赋值使得输入语句很是清晰。
const {SourceMapConsumer, SourceNode} = require("source-map");