ES6笔记(3)-- 解构赋值
系列文章 -- ES6笔记系列
解构赋值,即对某种结构进行解析,然后将解析出来的值赋值给相关的变量,常见的有数组、对象、字符串的解构赋值等
一、数组的解构赋值
function ids() { return [1, 2, 3]; } var [id1, id2, id3] = ids(); console.log(id1, id2, id3); // 1 2 3
如上,解析返回的数组,取出值并赋给相应的变量,这就是解构赋值
1. 还可以嵌套多层,只要相应的模式匹配了就能解析出来
var [a, [b, [c]]] = [1, [2, [3]]]; a // 1 b // 2 c // 3
2. 如若模式不匹配则报错
var [a, [b, [c]]] = [1, [2, 3]]; // Uncaught TypeError: undefined is not a function a
其实,解构赋值内部的实现方式使用到了ES6的Iterator迭代器,通过层层遍历,保证了相应值的获取
3. 解构不成功,但模式匹配了,相应值为undefined
var [a, b] = [1]; a // 1 b // undefined
4. 不需要匹配的位置可以置空
var [, b] = [1, 2]; b // 2
5. 使用...这个扩展运算符,匹配余下的所以值,形成一个数组(匹配不上则为[]),这个符号内部也用到了迭代器Iterator
var [a, ...b] = [1, 2, 3]; a // 1 b // [2, 3] var [a, ...b] = [1]; a // 1 b // []
6. 可以设置默认值,当相应的值严格等于undefined时,默认值会生效
var [a, b = [2, 3]] = [1]; a // 1 b // [2, 3]
var [a, b = [2, 3]] = [1, undefined]; a // 1 b // [2, 3]
var [a, b = [2, 3]] = [1, null]; a // 1 b // null
7. 惰性求值,对于默认值中出现函数调用模式的,只有默认值生效,函数才会调用,如下,foo函数将不会被调用
function foo() { console.log('hit'); } var [str = foo()] = [1]; str // 1
8. 可以方便的进行变量值的交换
var x = 1, y = 2; [x, y] = [y, x]; x // 2 y // 1
二、对象的解构赋值
与数组类似,对象也可以进行解构赋值
var {name, agee} = { name: 'jack', age: 22,
}; name // jack agee // undefined
如上,对象的解构赋值要求属性名称匹配正确,agee不匹配则变成undefined
1. 不过我们可以自定义属性名称,但要注意的是被赋值的只是我们自定义的属性名称,匹配的模式(项)并未被赋值
var {name, id: ID} = { name: 'jack', id: 1 }; ID // 1 id // Uncaught ReferenceError: id is not defined
更复杂的如
var { a0: { b0: { c0 } } } = { a0: { b0: { c0: 'cc', d0: 'dd' } } }; a0 // Uncaught ReferenceError: a0 is not defined b0 // Uncaught ReferenceError: b0 is not defined c0 // cc
2. 类似于数组,也可使用默认值
var {a:b = 2} = {}; b // 2 var {a:b = 2} = { a: 1 }; b // 1
3. 因为数组实际上也是个对象,所以
var {0: one, 1: two} = [1, 2]; two // 2
4. 可以方便地将某个对象上的属性方法一次性提取出来
var {map, slice} = Array.prototype; slice //
const {foo, bar} = require('./util');
5. 非声明时的解构赋值
非声明时,这里是指纯粹的解构赋值,如下代码
var a; {a} = { a: 1 }; // Uncaught SyntaxError: Unexpected token = a
以上代码报错了,但某些情况下我们还是需要直接赋值怎么办?
大括号{位于行首,匹配了}之后JS引擎就会认为{a}是一个代码块,所以等号就出问题了,解决方式是在行首放个括号(,即外包裹一层括号()
var a; ({a} = { a: 1 }); a // 1
括号的出现,让整个解构赋值的结构被看做一个代码块,而内部的{a}模式则可以正常匹配到
细心的盆友会注意到,右括号换个位置,报错了
var a; ({a}) = { a: 1 }; // Uncaught SyntaxError: Unexpected token ( a
6. 其实,解构赋值中括号的使用还是有讲究的
1) 不能使用括号的情况
1-1)变量声明语句中,不能带有括号
// 以下代码都会报错 var [(a)] = [1]; var {x: (c)} = {}; var ({x: c}) = {}; var {(x: c)} = {}; var {(x): c} = {}; var { o: ({ p: p }) } = { o: { p: 2 } };
1-2)函数参数中,模式不能带有括号
// 报错 function f([(z)]) { return z; } f([1])
1-3)赋值语句中,不能将整个模式,或嵌套模式中的一层,放在括号之中
var a; ({a}) = { a: 1 }; // Uncaught SyntaxError: Unexpected token ( a
2)可以使用括号的情况
可以使用括号的情况很好记,只有一种:赋值语句的非模式部分,可以使用括号
// 都正确 [(b)] = [3]; ({ p: (d) } = {}); [(b)] = ([3]);
三、字符串的解构赋值
字符串也可进行解构赋值,因为此时的字符串被转换成了类数组的对象,模式能够匹配起来,如
var [a, b] = 'str'; a // s b // t var {0:a, 1:b, length:len} = 'str'; a // s b // t len // 3
四、其他类型的解构赋值
1. 解构赋值的规则是,只要等号右边的值不是对象,就先尝试将其转为对象。如果转换之后的对象或原对象拥有Iterator接口,则可以进行解构赋值,否则会报错。
var {toString: s} = 1; s // var {toString: s} = true; s //
以上的数组和布尔值会转换成对象,toString模式匹配上了对象的toString属性,所以解构成功,而null或undefined却不能转换成此类对象,所以报错
var {toString: s} = null; s // Uncaught TypeError: Cannot match against 'undefined' or 'null'.
ES6引入了Iterator迭代器,集合Set或Generator生成器函数等都部署了这个Iterator接口,所以也可以用来进行解构赋值
2. 比如Set的解构赋值
var [a, b, c] = new Set([1, 2, 3]); a // 1 b // 2 c // 3
3. 函数参数的解构赋值
function calc([a, b, c = 10]) { console.log(a + b + c); // 13 } calc([1, 2]);