变量的解构赋值

2023-01-11 0 994

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

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务