JS 变量的解构赋值
定义
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
数组的解构赋值
以前
let a = 1
let b = 2
let c = 3
现在
let [a, b, c] = [1, 2, 3]
// 可以从数组中提取值,按照对应位置,对变量赋值。
完全解构,解构不成功,变量的值就等于 undefined。
let [foo, [[bar], baz]] = [1, [[2], 3]]
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"]
third // "baz"
let [x, , y] = [1, 2, 3]
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4]
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a']
x // "a"
y // undefined
z // []
不完全解构
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 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。
数组的解构默认值
解构赋值允许指定默认值。
let [foo = true] = []
foo // true
let [x, y = 'b'] = ['a'] // x='a', y='b'
let [x, y = 'b'] = ['a', undefined] // x='a', y='b'
ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined,默认值才会生效
let [x = 1] = [undefined]
x // 1
let [x = 1] = [null]
x // null 全等
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f() {
console.log('aaa')
return 5
}
let [x = f()] = [1] // f 函数没有执行
let [x = f()] = [undefined] // aaa x = 5
默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [x = 1, y = x] = [] // x=1 y=1
let [x = 1, y = x] = [2] // x=2 y=2
let [x = 1, y = x] = [1, 2] // x=1 y=2
let [x = y, y = 1] = [] // ReferenceError: y is not defined
对象的解构赋值
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" }
简写
let { foo, bar } = { foo: "aaa", bar: "bbb" }
foo // "aaa"
bar // "bbb"
//对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" }
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" }
baz // undefined
简写 2(变量名与属性名不一致)
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'
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
let { foo: baz } = { foo: "aaa", bar: "bbb" }
baz // "aaa"
foo // error: foo is not defined
let { foo: baz, foo: baz2 } = {foo:'aaa'}
baz //aaa
baz2 //aaa
对象的解构默认值
let {x = 3} = {}
x // 3
let {x, y = 5} = {x: 1}
x // 1
y // 5
let {x: y = 3} = {}
y // 3
let {x: y = 3} = {x: 5}
y // 5
let { message: msg = 'Something went wrong' } = {}
msg // "Something went wrong"
let {x = 3} = {x: undefined}
x // 3
let {x = 3} = {x: null}
x // null
let {foo} = {bar: 'baz'}
foo // undefined
let {foo: {bar}} = {baz: 'baz'}
// Cannot destructure property `bar` of 'undefined' or 'null'
嵌套结构的解构赋值
let obj = {p: ['Hello',{ y: 'World' }]}
let { p: [x, { y }] } = obj
x // "Hello"
y // "World"
这时 p 是模式,不是变量,因此不会被赋值。
如果想要 p 也被赋值可以这样写
let { p:p,p: [x, { y }] } = obj
//简写
let { p,p: [x, { y }] } = obj
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
更多例子
const node = {loc: {start: {line: 1,column: 5}}}
let { loc, loc: { start }, loc: { start: { line }} } = node
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
let obj = {}
let arr = []
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }) // 表达式而不是声明
obj // {prop:123}
arr // [true]
奇葩变量的解构赋值
// 错误的写法
let x
{x} = {x: 1}
// SyntaxError: syntax error
// 正确的写法
let x
({x} = {x: 1})
({} = [true, false])
({} = 'abc').
({} = [])
//语法是合法的,可以执行。
let arr = [1, 2, 3]
let {0 : first, [arr.length - 1] : last} = arr
first // 1
last // 3
上面代码对数组进行对象解构。数组 arr 的 0 键对应的值是 1,[arr.length - 1]就是 2 键,对应的值是 3。方括号这种写法,属于“属性名表达式”
字符窜的解构赋值
字符串被转换成了一个类似数组的对象
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
函数参数的解构赋值
function add([x, y]){
return x + y
}
add([1, 2]) // 3
[[1, 2], [3, 4]].map(([a, b]) => a + b)
// [ 3, 7 ]
[1, undefined, 3].map((x = 'yes') => x)
// [ 1, 'yes', 3 ]
也可以设置默认值
function 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]
// 可以看出是为等号前面的对象设置默认值
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined // TypeError
let { prop: y } = null // TypeError
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: JS 顶层对象
下一篇: TypeScript 常见问题
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论