一、數組的解構賦值
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 // 3let [ , , third] = ['foo', 'bar', 'baz']
third // 'baz'let [x, , y] = [1, 2, 3]
x // 1
y // 3let [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 undefinedconst [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 2const [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] = argumentsconsole.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