1. 字符串的重构表达式
ES6 容许依照很大商业模式,从字符串和第一类中抽取值,对表达式展开表达式,这被称作重构( Destructuring )。
早先,为表达式表达式,要间接选定值。
let a = 1,
b = 2,
c = 3
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
ES6 容许上面此种读法表达式,从字符串中抽取值,依照相关联边线,对表达式表达式。
let [a , b, c] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
本质上,此种读法属于“商业模式匹配”,只要等号两边的商业模式相同,左边的表达式就会被赋予相关联的值。
let [a, [b, [c]]] = [1, [2, [3]]]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
let [ , , third] = [1, 2, 3]
console.log(third) // 3
let [x, …y] = [1, 2, 3, 4]
console.log(x) // 1
console.log(y) // [2, 3, 4]
let [i, m, …j] = [only one]
console.log(i) // only one
console.log(m) // undefined
console.log(j) // []
如果重构不成功,表达式的值就为 undefined 。
let [a] = [],
[b, c] = [1]
console.log(a) // undefined
console.log(b) // 1
console.log(c) // undefined
等号左边,只匹配等号右边的一部分,此种情况被称作不完全重构。
let [x, y] = [1, 2, 3]
console.log(x) // 1
console.log(y) // 2
let [a, [b], c] = [1, [2, 3], 4]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 4
如果等号的右边不是字符串(或者严格地说,不是可遍历的结构),那么将会报错。
let [a] = 1, // Uncaught TypeError: 1 is not iterable
[b] = false, // Uncaught TypeError: false is not iterable
[c] = NaN, // Uncaught TypeError: NaN is not iterable
[d] = undefined, // Uncaught TypeError: undefined is not iterable [e] = null, // Uncaught TypeError: null is not iterable
[f] = {} // Uncaught TypeError: {} is not iterable
对于 Set 结构,也可以使用字符串的重构表达式。
let [x, y, z] = new Set([a, b, c])
console.log(x) // “a”
console.log(y) // “b”
console.log(z) // “c”
事实上,只要某种数据结构具有 Iterator (迭代器) 接口,都可以采用字符串形式的重构表达式。
Generator 函数具有 Iterator 接口,重构表达式会依次
function* fibs() {
let a = 0,
b = 1
while (true) {
yield a;
[a, b] = [b, a + b]
}
}
let [h, i, j, k, l, m] = fibs()
console.log(h) // 0
console.log(i) // 1
console.log(j) // 1
console.log(k) // 2
console.log(l) // 3
console.log(m) // 5
默认值
重构表达式容许选定默认值。
let [a = 1] = [],
[b, c = x] = [y],
[d, e = n] = [m, undefined]
console.log(a) // 1
console.log(b) // y
console.log(c) // x
console.log(d) // m
console.log(e) // n
ES6 内部使用严格相等运算符( === ),判断一个边线是否有值。所以,只有当一个字符串成员严格等于 undefined ,默认值才会生效。
let [x = 1 ] = [undefined],
[y = 1 ] = [null]
console.log(x) // 1
console.log(y) // null
如果默认值是一个表达式,那么这个这个表达式是惰性求值的,即只在用到的时候(即等号右边的值 === undefined 时),才会求值。
function f() {
return a
}
let [a = f()] = [1],
[b = f()] = [],
[c = f()] = [undefined],
[d = f()] = [null]
console.log(a) // 1
console.log(b) // a
console.log(c) // a
console.log(d) // null
如果等号右边可以取到值(除了 undefined 外),那么左边的默认值表达式是不会执行的(惰性)。
let m = 2,
[a = 1 + m] = [1],
[b = 1 + m] = [],
[c = 1 + m] = [undefined],
[d = 1 + m] = [null]
console.log(a) // 1
console.log(b) // 3
console.log(c) // 3
console.log(d) // null
默认值也可以引用重构表达式的其他表达式,但该表达式要在使用前已经声明。
let [a = 1, b = a] = [],
[c = 1, d = c] = [2],
[e = 1, f = e] = [1, 2],
[g = 1, h = g] = [undefined],
[i = j, j = 1] = [] // Uncaught ReferenceError: j is not defined
console.log(a) // 1
console.log(b) // 1
console.log(c) // 2
console.log(d) // 2
console.log(e) // 1
console.log(f) // 2
console.log(g) // 1
console.log(h) // 1
2.第一类的重构表达式
重构不仅可以用于字符串,还可以用于第一类。
第一类的重构与字符串有一个重要的不同。字符串的元素是按次序排列的,表达式的取值由它的边线决定;而第一类的属性没有次序,表达式要与属性同名,才能取到正确的值。
let {a, b} = {a: 1, b: 2},
{c, d} = {d: 3, c: 4},
{e} = {f: 5, e: 6}
console.log(a) // 1
console.log(b) // 2
console.log(c) // 4
console.log(d) // 3
console.log(e) // 6
表达式名与属性名不一致。
let {a: b} = {a: 1, c: 2}
console.log({a: b}.a) // 1
console.log(b) // 1
第一类的重构表达式的内部机制,是先找到同名属性,然后再赋给相关联的表达式。真正被表达式的是后者,而非前者。
let obj = {a: 1, b: 2},
{a: one, b: two} = obj
console.log(one) // 1
console.log(two) // 2
console.log(obj.a) // 1
console.log(obj.b) // 2
与字符串一样,重构也可以用于嵌套结构的第一类。
let obj = {
p: [
Hello,
{y: World}
]
},
{p: [x, {y}]} = obj
console.log(x) // Hello
console.log(y) // World
注意, 这里的 p 是商业模式,不是表达式,因此不会被表达式。如果 p 也要作为表达式表达式,如下。
let obj = {
p: [
Hello,
{y: World}
]
},
{p, p: [x, {y}]} = obj
console.log(x) // Helloconsole.log(y) // World
console.log(p) // [“Hello”, {y: “World”}]
注意区分商业模式与表达式,商业模式不会被表达式,表达式才会被表达式。
const obj = {
a: {
b: {
c: 1,
d: 5
}
}
},
{a, a: {b}, a: {b: {c}}} = obj
console.log(a) // {b: {c: 1, d: 5}}
console.log(b) // {c: 1, d: 5}console.log(c) // 1
嵌套表达式。
let obj = {},
arr = [];
({a: obj.a, b: arr[0]} = {a: 1, b: 2})
console.log(obj) // {a: 1}
console.log(arr) // [2]
第一类的重构表达式也可以选定默认值。
var {a = 1} = {}
console.log(a) // 1
var {a, b = 2} = {a: 1}
console.log(a) // 1
console.log(b) // 2
var {a: b = 1} = {}
// console.log(a) // Uncaught ReferenceError: a is not defined
console.log(b) // 1
var {a, a: b = 1} = {}
console.log(a) // undefined
console.log(b) // 1
var {a: b = 1} = {a: 2}
// console.log(a) // Uncaught ReferenceError: a is not definedconsole.log(b) // 2
var {a, a: b = 1} = {a: 2}
console.log(a) // 2console.log(b) // 2
var {a: b = Hello World} = {}
// console.log(a) // Uncaught ReferenceError: a is not definedconsole.log(b) // Hello World
var {a, a: b = Hello World} = {}
console.log(a) // undefined
console.log(b) // Hello World
默认值生效的条件是,第一类的属性严格等于 undefined 。
var {a = 1} = {}
console.log(a) // 1
var {a = 2} = {a: undefined}
console.log(a) // 2
var {a = 3} = {a: null}
console.log(a) // null
var {a = 4} = {a: }
console.log(a) // 空字符串
如果重构失败,表达式的值等于 undefined 。
let {a} = {b: 1}
console.log(a) // undefined
// console.log(b) // Uncaught ReferenceError: b is not defined
如果重构商业模式是嵌套的第一类,而且子第一类所在的父属性不存在,那么将会报错。
{
let {a: b} = {d: 1}
// console.log(a)
console.log(b) // undefined // console.log(d)
}
{
let {a: {b: c}} = {d: 1} // Uncaught TypeError: Cannot destructure property `b` of undefined or null.}
{
let {a: []} = {d: 1} // Uncaught TypeError: Cannot read property Symbol(Symbol.iterator) of undefined}
父属性不存在,赋给他的值即为 undefined ,对 undefined 再去取子第一类,则抛出错误信息。
{
let a = {b: 2}
console.log(a) // {b: 2}
// console.log(b) // Uncaught ReferenceError: b is not defined console.log(a.c) // undefined
// console.log(a.c.c) // Uncaught TypeError: Cannot read property c of undefined}
如果将一个已经声明的表达式用于重构表达式,需要注意 {} 会被解析为代码块,导致抛出语法错误。可以将重构表达式语句放在圆括号里面,来让代码正常执行。
{
let a
{a} = {a: 1} // Uncaught SyntaxError: Unexpected token =
}
{
let a
({a} = {a: 1})
console.log(a) // 1
}
重构表达式容许等号左边的商业模式之中,不放置任何表达式名。虽然表达式毫无意义,但在语法层面是合法的,可执行的。
{
({} = [1, 2])
({} = ab)
({} = [])
}
第一类的重构表达式,可以很方便地将现有第一类的方法,表达式到某个表达式。
{
let {floor, ceil} = Math
console.log(floor) // f floor() { [native code] } console.log(ceil) // f ceil() { [native code] }
}
由于字符串本质是特殊的第一类,因此可以对字符串展开第一类属性的重构。
{
let arr = [a, b, c],
{0: x, 1: y, [arr.length – 1]: z} = arr
console.log(x) // a
console.log(y) // b
console.log(z) // c
}
3.字符串的重构表达式
字符串也可以重构表达式。
这是因为,字符串可以被转换成一个类似字符串的第一类。
{
const [a, b, c, d] = 1234
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
console.log(d) // 4
}
类似字符串的第一类都有一个 length 属性,所以还可以对这个属性重构表达式。
{
let {length: len} = abcd
console.log(len) // 4
}
4.数值和布尔值的重构表达式
重构表达式时,如果等号右边是数值和布尔值,则会先转为第一类。
字符串和布尔值的包装第一类都有 toString 属性。
{
let {toString: x} = 123
console.log(x) // f toString() { [native code] }
let {toString: y} = true
console.log(y) // f toString() { [native code] }}
重构表达式的规则是,只要等号右边的值不是第一类或字符串,就先将其转为第一类。由于 undefined 和 null 无法转为第一类,所以对它们展开重构表达式,都会报错。
{
let {x: y} = undefined // Uncaught TypeError: Cannot destructure property `x` of undefined or null.
}
{
let {x: y} = null // Uncaught TypeError: Cannot destructure property `x` of undefined or null.}
5.函数参数的重构表达式
函数的参数也可以使用重构表达式。
{
function add([x, y]) {
return x + y
}
let arr = [1, 2]
add(arr) // 3
}
{
let a = [1, 2],
b = [3, 4],
c = [a, b]
c.map(([x, y]) => x + y) // [3, 7]}
函数参数的重构也可以使用默认值。
为 x 、 y 设置默认值。
{
function move ({x = 0, y = 0} = {}) {
return [x, y]
}
move() // [0, 0]
move({}) // [0, 0]
move({x: 1}) // [1, 0]
move({y: 2}) // [0, 2]
move({x: 1, y: 2}) // [1, 2]
}
为函数的参数设置默认值。
{
function move ({x, y} = {x: 0, y: 0}) {
return [x, y]
}
move() // [0, 0]
move({}) // [undefined, undefined]
move({x: 1}) // [1, undefined] move({y: 2}) // [undefined, 2]
move({x: 1, y: 2}) // [1, 2]
}
undefined 就会触发函数参数的默认值。
{
let arr = [1, 2, 3],
brr = [1, undefined, 3]
crr = arr.map((x = yes) => x)
drr = brr.map((x = yes) => x)
console.log(arr) // [1, 2, 3]
console.log(brr) // [1, undefined, 3]
console.log(crr) // [1, 2, 3]
console.log(drr) // [1, “yes”, 3]
}
6.圆括号问题
ES6 规定,只要有可能导致重构的歧义,就不得使用圆括号。
不能使用圆括号的情况
1.表达式声明语句
// 全部报错。 Uncaught SyntaxError: Unexpected token (
{
let [(x)] = [1],
{(x): y} = {},
{x: (y)} = {},
{(x: y)} = {},
{x: ({y: y})} = {}
}
表达式声明语句,商业模式使用圆括号会抛出错误。
{
let ({x: y}) = {} // Uncaught ReferenceError: let is not defined}
2.函数参数
函数参数也属于表达式声明,所以带有圆括号会抛出错误。
{
function f([(a)]) { // Uncaught SyntaxError: Unexpected token ( return a
}
}
{
function f([a, (b)]) { // Uncaught SyntaxError: Unexpected token (
return b
}
}
3.表达式语句的商业模式
{
{(a): b} = {} // Uncaught SyntaxError: Unexpected token :
}
{
{a: (b)} = {} // Uncaught SyntaxError: Unexpected token =}
{
{(a: b)} = {} // Uncaught SyntaxError: Unexpected token :
}
{
({a: b}) = {} // Uncaught SyntaxError: Unexpected token (}
可以使用圆括号的情况
表达式语句的非商业模式部分,使用圆括号不会报错。
{
[(a)] = [1]
console.log(a) // 1
}
{
[(parseInt.b)] = [1]
}
{
({a: (b)} = {})
// console.log(a) // Uncaught ReferenceError: a is not defined
console.log(b) // undefined
}
7.用途
1.交换表达式的值
{
let a = 1,
b = 2
console.log(a, b); // 1 2
[a, b] = [b, a]
console.log(a, b) // 2 1
}
排序中,比较后交换值。
{
let arr = [3, 2, 5, 4, 1, 7, 8]
for(let i = 0; i < arr.length; i++) {
for(let j = 0; j < arr.length; j ++) {
if (arr[i] < arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
}
console.log(arr) // [1, 2, 3, 4, 5, 7, 8]
}
2.从函数返回多个值
函数只能返回一个值,如果要返回多个值,则需要把它们放在字符串或第一类里返回。利用重构表达式,取出这些值就非常方便。
// 返回值为字符串
{
function f () {
return [a, b, c]
}
let [x, y, z] = f()
console.log(x, y, z) // a b c
}
// 返回值为第一类
{
function f () {
return {
x: 1,
y: 2,
z: 3
}
}
let {x, y, z} = f()
console.log(x, y, z) // 1 2 3}
3.函数参数的定义
重构表达式可以将一组参数与表达式名相关联起来。
// 参数是一组有次序的值 字符串
{
function f ([x, y, z]) {
return [x, y, z]
}
let [a, b, c] = f([1, 2, 3])
console.log(a, b, c) // 1 2 3
}
// 参数是一组无次序的值 第一类
{
function f ({x, y, z}) {
return {
x,
y,
z
}
}
let {x, y, z} = f({z: 3, y: 2, x: 1})
console.log(x, y, z) // 1 2 3
}
4.抽取 JSON 数据
{
let json = {
code: 0,
msg: 登录成功,
data: {
list: [1, 2, 3]
}
},
{code, msg, data: {list: arr}} = json
console.log(code, msg, arr) // 0 “登录成功” [1, 2, 3]
}
5.函数参数的默认值
选定参数的默认值,就可以省略在函数体内部写 var name =config.name || admin 这样的语句。
{
function f ({
name = admin,
age = 18,
sex = 男
} = {}) {
console.log(name, age, sex)
}
f() // admin 18 男
f({}) // admin 18 男
f({name: xie}) // xie 18 男
f({age: 16}) // admin 16 男
f({sex: 女, age: 2, name: tang}) // tang 2 女
}
6.遍历 Map 结构
具有 Iterator (迭代器)属性的第一类,都可以用 for … of 遍历。
Map 结构原生支持 Itera
{
const map = new Map()
map.set(first, one)
map.set(second, two)
console.log(map) // {“first” => “one”, “second” => “two”}
for (let [key, value] of map) {
console.log(`${key} is ${value}`)
}
// first is one
// second is two
for (let [key] of map) {
console.log(`${key} is `)
}
// first is // second is
for (let [, value] of map) {
console.log(` is ${value}`)
}
// is one
// is two
}
7.引入模块的选定方法
加载模块时,可以选定引入模块的部分方法,即按需加载。
const {Button, Select} = require(element-ui)
—— 2018.05.21 17:17