一、数组的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
1、基本使用
遵循 “模式匹配” ,索引值相同的完成赋值
// 为变量赋值,只能直接指定值。
let a = 1
let b = 2
let c = 3
// 解构赋值:从数组中提取值,按照对应位置,对变量赋值。
let [a, b, c] = [1, 2, 3]
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 // []
2、默认值
(1)默认值的基本用法
const [a, b] = []
console.log(a, b) // undefined undefined
const [a = 1, b = 2] = []
console.log(a, b) // 1 2
(2)默认值的生效条件
ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于 undefined,默认值才会生效。
const [a = 1, b = 2] = [3, 0] // 3 0
const [a = 1, b = 2] = [3, null] // 3 null
const [a = 1, b = 2] = [3] // 3 2
const [x = 1] = [undefined] // 1
const [x = 1] = [null] // null
(3)默认值表达式
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
// x 能取到值,所以函数 f 根本不会执行
function f() {
console.log('aaa')
}
let [x = f()] = [1]
(4)默认值引用
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = [] // 1 1
let [x = 1, y = x] = [2] // 2 2
let [x = 1, y = x] = [1, 2] // 1 2
let [x = y, y = 1] = [] // ReferenceError: Cannot access 'y' before initialization (x 用 y 做默认值时,y 还没有声明)
3、数组解构赋值的应用
(1)类数组对象 arguments
function func() {
const [a, b] = arguments
console.log(a, b) // 1 2
}
func(1, 2)
(2)NodeList
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NodeList</title>
</head>
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
const [p1, p2, p3] = document.querySelectorAll('p')
console.log(p1, p2, p3)
/*
<p>1</p>
<p>2</p>
<p>3</p>
*/
</script>
</body>
</html>
(3)函数参数的解构赋值
const array = [1, 1]
// const add = arr => arr[0] + arr[1]
const add = ([x = 0, y = 0]) => x + y
console.log(add(array)) // 2
console.log(add([])) // 0
[[1, 2], [3, 4]].map(([a, b]) => a + b)
// [ 3, 7 ]
(4)交换变量的值
let x = 2, y = 1
// 原来
let tmp = x
x = y
y = tmp
// 现在
[x, y] = [y, x] // [x, y] = [1, 2]
console.log(x, y) // 1 2
(5)跳过某项值使用逗号隔开
在解构数组时,可以忽略不需要解构的值,可以使用逗号对解构的数组进行忽略操作,这样就不需要声明更多的变量去存值了:
var [a, , , b] = [10, 20, 30, 40]
console.log(a) // 10
console.log(b) // 40
(6)剩余参数中的使用
通常情况下,需要把剩余的数组项作为一个单独的数组,这个时候我们可以借助展开语法把剩下的数组中的值,作为一个单独的数组,如下:
var [a, b, ...rest] = [10, 20, 30, 40, 50]
console.log(a) // 10
console.log(b) // 20
console.log(rest) // [30, 40, 50]
在 rest 的后面不能有 逗号 不然会报错,程序会认出你后面还有值。…rest 是剩余参数的解构,所以只能放在数组的最后,在它之后不能再有变量,否则则会报错。
二、对象的解构赋值
1、基本使用
对象的解构和数组基本类似,对象解构的变量是在 {} 中定义的。
遵循 “模式匹配” ,属性名相同的完成赋值。
// 对象没有索引,但对象有更明确的键,通过键可以很方便地去对象中取值
let {
foo, bar } = {
foo: 'aaa', bar: 'bbb' }
foo // 'aaa'
bar // 'bbb'
let {
bar, foo, baz } = {
foo: 'aaa', bar: 'bbb' }
foo // 'aaa'
bar // 'bbb'
baz // undefined
如果变量名与属性名不一致,必须写成下面这样。
// foo 是匹配的模式,baz 才是变量。真正被赋值的是变量 baz,而不是模式 foo。
let {
foo: baz } = {
foo: 'aaa', bar: 'bbb' }
baz // 'aaa'
foo // error: foo is not defined
2、默认值
默认值生效的条件是,对象的属性值严格等于 undefined。
如果默认值是表达式,默认值表达式是惰性求值的。
var {
a = 10, b = 5 } = {
a: 3 } // a = 3, b = 5
var {
a = 10, b = 5 } = {
a: 3, b: undefined } // a = 3, b = 5
var {
a = 10, b = 5 } = {
a: 3, b: null } // a = 3, b = null
3、对象解构赋值的应用
(1)对象作为函数参数
// 之前
const logPersonInfo = user => console.log(user.name, user.age)
logPersonInfo({
name: 'jerry', age: 18 }) // jerry 18
// 之后
const logPersonInfo = ({
age = 21, name = 'tom' }) => console.log(name, age);
logPersonInfo({
name: 'jerry', age: 18}) // jerry 18
logPersonInfo({
}) // tom 21
(2)从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3]
}
let [a, b, c] = example()
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
}
}
let {
foo, bar } = example()
(3)复杂嵌套(多重解构赋值)
let obj = {
p: [
'Hello',
{
y: 'World' }
]
}
// 这时 p 是模式,不是变量,因此不会被赋值。
let {
p: [x, {
y }] } = obj
x // 'Hello'
y // 'World'
// p 作为变量赋值
let {
p, p: [x, {
y }] } = obj
x // 'Hello'
y // 'World'
p // ['Hello', {y: 'World'}]
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
}
// 三次解构赋值,分别是对 loc、start、line 三个属性的解构赋值。
// 注意,最后一次对 line 属性的解构赋值之中,只有 line 是变量,loc 和 start 都是模式,不是变量.
let {
loc, loc: {
start }, loc: {
start: {
line }} } = node
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
(4)剩余参数中的使用
在对象的解构中也可以使用剩余参数,对象中没有解构的剩余属性做聚合操作,生成一个新的对象。
const {
a, c, ...rest } = {
a: 1, b: 2, c: 3, d: 4 }
console.log(a) // 1
console.log(c) // 3
console.log(rest) // { b: 2, d: 4 }
4、注意点
(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x
{
x } = {
x: 1 }
// SyntaxError: syntax error
上面代码的写法会报错,因为 JavaScript 引擎会将 {x} 理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
// 正确的写法
let x
({
x } = {
x: 1 })
上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。
(2)解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。
({
} = [true, false])
({
} = 'abc')
({
} = [])
上面的表达式虽然毫无意义,但是语法是合法的,可以执行。
(3)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
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] = 'hello'
console.log(a, b, c) // h e o
// 对象形式解构赋值
// 类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
const {
0: a, 1: b, 4: o, length : len } = 'hello'
console.log(a, b, o, len) // h e o 5
四、数值和布尔值的解构赋值
只能按照对象的形式来解构赋值。(先自动将等号右边的值转为对象)
// 转化后的对象里没有任何的属性(没有 123 这个属性,也没有 true 这个属性)和方法,
// 所有的属性和方法都在它的继承 __proto__ 中,比如 toString 方法就是继承来的。
new Number(123)
new Boolean(true)
// 里面的值只能是默认值,继承的方法倒是可以取到
const {
a = 1, toString: s } = 123
console.log(a, s) // 1 [Function: toString]
// 里面的值只能是默认值,继承的方法倒是可以取到
const {
b = 1, toString } = true;
console.log(b, toString) // 1 [Function: toString]
五、undefined 和 null 没有解构赋值
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于 undefined和 null 无法转为对象,所以对它们进行解构赋值,都会报错。
let {
prop: x } = undefined // TypeError
let {
prop: y } = null // TypeError