[JS] ECMAScript 6 - Destructuring
C#里没有这种变态的方法。
虽然变态,但看起来不错的样子。
变量的解构赋值
- 完全解构:本质上,这种写法属于“模式匹配”,;
- 不完全解构:同时支持“不完全解构”
let [x, y] = [1, 2, 3]; x // 1 y // 2 let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4
- 数组:左边是数组,能遍历,但右边的不能,将报错。
// 报错 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {};
- 集合:事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
let [x, y, z] = new Set(['a', 'b', 'c']); x // "a"
函数:fibs
是一个 Generator 函数,原生具有 Iterator 接口。解构赋值会依次从这个接口获取值。
function* fibs() { let a = 0; let b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } let [first, second, third, fourth, fifth, sixth] = fibs(); sixth // 5
- 默认值:当一个数组成员严格等于
undefined
,默认值才会生效
let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; //null
不严格等于undefined
x // null
若取到值,则不会触发默认值。
对象的解构赋值
对象的属性没有次序,变量必须与属性同名,才能取到正确的值。【字典匹配模式,不是通过位置】
简单匹配
- key的匹配:
let { bar, foo } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" let { baz } = { foo: "aaa", bar: "bbb" }; baz // undefined
- value的配置:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world'
嵌套匹配
冒号代表的“模式”:如下,这时p
是模式,不是变量,因此不会被赋值。如果p
也要作为变量赋值,可以写成下面这样【见2】。
let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p: [x, { y }] } = obj;let { p, p: [x, { y }] } = obj; <----【2】
x // "Hello"
y // "World"
强化理解:”模式“就是路径的效果。
const node = { loc: { start: { line: 1, column: 5 } } }; let { loc, loc: { start }, loc: { start: { line }} } = node;
loc // Object {start: Object} start // Object {line: 1, column: 5} line // 1
模式的计算:不是圆括号,是方括号。
let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; first // 1 last // 3
危险区:路径若是”越界“了,报错。
// 报错 let {foo: {bar}} = {baz: 'baz'};// foo
这时等于undefined
,再取子属性就会报错
let _tmp = {baz: 'baz'};_tmp.foo.bar // 报错,这就好理解了,没有路径,越界了
危险区:只有不将大括号写在行首,避免 JavaScript 将其解释为代码块。
let x;
({x} = {x: 1}); // 可见圆括号的牛逼之处,such as ({} = 'abc'); 一个毫无意义的写法,但语法没问题!
扩展 - 可以理解为对象
- 字符串的解构赋值
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" let {length : len} = 'hello'; len // 5
- 数值和布尔值的解构赋值
let {toString: s} = 123; s === Number.prototype.toString // true let {toString: s} = true; s === Boolean.prototype.toString // true
- undefined和null解构赋值
let { prop: x } = undefined; // TypeError let { prop: y } = null; // TypeError
函数参数的解构赋值
支持多套参数:参数放在数组里。
[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]
默认值:
unction 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]
function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0]
[1, undefined, 3].map((x = 'yes') => x); // [ 1, 'yes', 3 ]
-
- 参数默认值可以与解构赋值的默认值,结合起来使用。
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) { console.log(method); } fetch('http://example.com', {}) // "GET" fetch('http://example.com') // 有了双重默认值就不会报错了 // 报错
练习:
// 写法一 function m1({x = 0, y = 0} = {}) { #设置了对象解构赋值的默认值 return [x, y]; } // 写法二 function m2({x, y} = { x: 0, y: 0 }) { return [x, y]; }
几个实验:
// 函数没有参数的情况 m1() // [0, 0] m2() // [0, 0] // x 和 y 都有值的情况 m1({x: 3, y: 8}) // [3, 8] m2({x: 3, y: 8}) // [3, 8] // x 有值,y 无值的情况 m1({x: 3}) // [3, 0] m2({x: 3}) // [3, undefined] // x 和 y 都无值的情况 m1({}) // [0, 0]; m2({}) // [undefined, undefined] m1({z: 3}) // [0, 0] m2({z: 3}) // [undefined, undefined]
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊