JS 变量的解构赋值

发布于 2024-09-21 02:52:42 字数 6121 浏览 6 评论 0

定义

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

木格

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

emdigitizer10

文章 0 评论 0

残龙傲雪

文章 0 评论 0

奢望

文章 0 评论 0

微信用户

文章 0 评论 0

又爬满兰若

文章 0 评论 0

独孤求败

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文