undefined
觸發預設值...
)解構是一種從儲存在(可能巢狀的)物件和陣列中的資料中萃取多個值的方法。它可以用於接收資料的位置(例如指定式的左側)。如何萃取值是透過模式指定的(請繼續閱讀以取得範例)。
解構物件
const
obj
=
{
first
:
'Jane'
,
last
:
'Doe'
};
const
{
first
:
f
,
last
:
l
}
=
obj
;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const
{
first
,
last
}
=
obj
;
// first = 'Jane'; last = 'Doe'
解構有助於處理回傳值
const
obj
=
{
foo
:
123
};
const
{
writable
,
configurable
}
=
Object
.
getOwnPropertyDescriptor
(
obj
,
'foo'
);
console
.
log
(
writable
,
configurable
);
// true true
陣列解構(適用於所有可迭代值)
const
iterable
=
[
'a'
,
'b'
];
const
[
x
,
y
]
=
iterable
;
// x = 'a'; y = 'b'
解構有助於處理回傳值
const
[
all
,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
'2999-12-31'
);
解構可以在以下位置使用(我展示陣列模式來示範;物件模式也能正常運作)
// Variable declarations:
const
[
x
]
=
[
'a'
];
let
[
x
]
=
[
'a'
];
var
[
x
]
=
[
'a'
];
// Assignments:
[
x
]
=
[
'a'
];
// Parameter definitions:
function
f
([
x
])
{
···
}
f
([
'a'
]);
您也可以在 for-of
迴圈中解構
const
arr
=
[
'a'
,
'b'
];
for
(
const
[
index
,
element
]
of
arr
.
entries
())
{
console
.
log
(
index
,
element
);
}
// Output:
// 0 a
// 1 b
為了完全了解解構是什麼,讓我們先檢視其更廣泛的背景。
JavaScript 有用於建構資料的運算,一次一個屬性
const
obj
=
{};
obj
.
first
=
'Jane'
;
obj
.
last
=
'Doe'
;
相同的語法可以用於萃取資料。同樣地,一次一個屬性
const
f
=
obj
.
first
;
const
l
=
obj
.
last
;
此外,還有用於一次建構多個屬性的語法,透過物件文字
const
obj
=
{
first
:
'Jane'
,
last
:
'Doe'
};
在 ES6 之前,沒有對應的機制來萃取資料。這就是解構的用途,它讓您可以透過物件模式從物件中萃取多個屬性。例如,在指定運算的左手邊
const
{
first
:
f
,
last
:
l
}
=
obj
;
您也可以透過模式解構陣列
const
[
x
,
y
]
=
[
'a'
,
'b'
];
// x = 'a'; y = 'b'
解構涉及以下兩個部分
解構目標是下列三種模式之一
x
{ first: «pattern», last: «pattern» }
[ «pattern», «pattern» ]
這表示您可以任意深度巢狀模式
const
obj
=
{
a
:
[{
foo
:
123
,
bar
:
'abc'
},
{}],
b
:
true
};
const
{
a
:
[{
foo
:
f
}]
}
=
obj
;
// f = 123
如果您解構物件,您只會提到您有興趣的屬性
const
{
x
:
x
}
=
{
x
:
7
,
y
:
3
};
// x = 7
如果您解構陣列,您可以選擇只萃取一個前綴
const
[
x
,
y
]
=
[
'a'
,
'b'
,
'c'
];
// x='a'; y='b';
在指定 pattern = someValue
時,pattern
如何存取 someValue
中的內容?
物件樣式會在存取屬性之前,將解構來源轉換為物件。這表示它適用於基本值
const
{
length
:
len
}
=
'abc'
;
// len = 3
const
{
toString
:
s
}
=
123
;
// s = Number.prototype.toString
轉換為物件並非透過 Object()
執行,而是透過內部運算 ToObject()
執行。這兩個運算處理 undefined
和 null
的方式不同。
Object()
會將基本值轉換為包裝物件,並保留物件不變
> typeof Object('abc')
'object'
> var obj = {};
> Object(obj) === obj
true
它也會將 undefined
和 null
轉換為空物件
> Object(undefined)
{}
> Object(null)
{}
相反地,ToObject()
如果遇到 undefined
或 null
,就會擲回 TypeError
。因此,下列解構會失敗,甚至在解構存取任何屬性之前
const
{
prop
:
x
}
=
undefined
;
// TypeError
const
{
prop
:
y
}
=
null
;
// TypeError
因此,您可以使用空物件樣式 {}
來檢查值是否可轉換為物件。正如我們所見,只有 undefined
和 null
不可轉換
({}
=
[
true
,
false
]);
// OK, Arrays are coercible to objects
({}
=
'abc'
);
// OK, strings are coercible to objects
({}
=
undefined
);
// TypeError
({}
=
null
);
// TypeError
表達式周圍的括號是必要的,因為陳述式在 JavaScript 中不能以大括號開頭 (稍後會說明詳細資訊).
陣列解構使用迭代器來取得來源的元素。因此,您可以對任何可迭代值執行陣列解構。我們來看可迭代值的範例。
字串是可迭代的
const
[
x
,...
y
]
=
'abc'
;
// x='a'; y=['b', 'c']
別忘了字串的迭代器會傳回碼點(「Unicode 字元」,21 位元),而不是碼元(「JavaScript 字元」,16 位元)。(有關 Unicode 的更多資訊,請參閱「Speaking JavaScript」中的「第 24 章 Unicode 和 JavaScript」章節。)例如
const
[
x
,
y
,
z
]
=
'a\uD83D\uDCA9c'
;
// x='a'; y='\uD83D\uDCA9'; z='c'
您無法透過索引存取 Set 的元素,但您可以透過迭代器存取。因此,陣列解構適用於 Set
const
[
x
,
y
]
=
new
Set
([
'a'
,
'b'
]);
// x='a'; y='b’;
Set
迭代器總是按插入順序傳回元素,這就是前一個解構的結果總是相同的緣故。
如果一個值有一個方法,其金鑰為 `Symbol.iterator`,且會傳回一個物件,則該值為可迭代的。如果要解構的值不可迭代,陣列解構會擲出 `TypeError`
let
x
;
[
x
]
=
[
true
,
false
];
// OK, Arrays are iterable
[
x
]
=
'abc'
;
// OK, strings are iterable
[
x
]
=
{
*
[
Symbol
.
iterator
]()
{
yield
1
}
};
// OK, iterable
[
x
]
=
{};
// TypeError, empty objects are not iterable
[
x
]
=
undefined
;
// TypeError, not iterable
[
x
]
=
null
;
// TypeError, not iterable
即使在存取可迭代元素之前,也會擲出 `TypeError`,這表示你可以使用空的陣列模式 `[]` 來檢查一個值是否可迭代
[]
=
{};
// TypeError, empty objects are not iterable
[]
=
undefined
;
// TypeError, not iterable
[]
=
null
;
// TypeError, not iterable
預設值 是模式的選用功能。如果在來源中找不到任何值,它們會提供一個後備值。如果某個部分(一個物件屬性或一個陣列元素)在來源中沒有對應項,它會與下列項目配對
undefined
(否則)我們來看一個範例。在以下解構中,索引 0 處的元素在右手邊沒有對應項。因此,解構會繼續進行,將 `x` 與 3 配對,這會導致 `x` 被設定為 3。
const
[
x
=
3
,
y
]
=
[];
// x = 3; y = undefined
你也可以在物件模式中使用預設值
const
{
foo
:
x
=
3
,
bar
:
y
}
=
{};
// x = 3; y = undefined
undefined
會觸發預設值 如果某個部分確實有一個對應項,但該對應項是 `undefined`,也會使用預設值
const
[
x
=
1
]
=
[
undefined
];
// x = 1
const
{
prop
:
y
=
2
}
=
{
prop
:
undefined
};
// y = 2
這種行為的理由在下一章中說明,請參閱 參數預設值。
預設值本身只會在需要時計算。換句話說,這個解構
const
{
prop
:
y
=
someFunc
()}
=
someValue
;
等同於
let
y
;
if
(
someValue
.
prop
===
undefined
)
{
y
=
someFunc
();
}
else
{
y
=
someValue
.
prop
;
}
你可以觀察,如果你使用 `console.log()`
> function log(x) { console.log(x); return 'YES' }
> const [a=log('hello')] = [];
> a
'YES'
> const [b=log('hello')] = [123];
> b
123
在第二個解構中,預設值不會觸發,也不會呼叫 `log()`。
預設值可以參照任何變數,包括同一個模式中的其他變數
const
[
x
=
3
,
y
=
x
]
=
[];
// x=3; y=3
const
[
x
=
3
,
y
=
x
]
=
[
7
];
// x=7; y=7
const
[
x
=
3
,
y
=
x
]
=
[
7
,
2
];
// x=7; y=2
但是,順序很重要:變數 `x` 和 `y` 從左到右宣告,如果在宣告之前存取它們,會產生 `ReferenceError`
const
[
x
=
y
,
y
=
3
]
=
[];
// ReferenceError
到目前為止,我們只看過變數的預設值,但你也可以將預設值與模式關聯
const
[{
prop
:
x
}
=
{}]
=
[];
這代表什麼意思?回想預設值的規則:如果某個部分在來源中沒有配對,解構會繼續使用預設值。
索引 0 的元素沒有配對,這就是解構繼續使用
const
{
prop
:
x
}
=
{};
// x = undefined
如果你將模式 { prop: x }
替換成變數 pattern
,你會更容易看出為什麼會這樣。
const
[
pattern
=
{}]
=
[];
讓我們進一步探討模式的預設值。在以下範例中,我們透過預設值 { prop: 123 }
為 x
指定一個值
const
[{
prop
:
x
}
=
{
prop
:
123
}]
=
[];
由於索引 0 的陣列元素在右手邊沒有配對,因此解構繼續如下,而 x
設為 123。
const
{
prop
:
x
}
=
{
prop
:
123
};
// x = 123
不過,如果右手邊有索引 0 的元素,則 x
不會以這種方式指定值,因為這樣不會觸發預設值。
const
[{
prop
:
x
}
=
{
prop
:
123
}]
=
[{}];
這種情況下,解構繼續使用
const
{
prop
:
x
}
=
{};
// x = undefined
因此,如果你想要在物件或屬性遺失時讓 x
為 123,你需要為 x
本身指定一個預設值
const
[{
prop
:
x
=
123
}
=
{}]
=
[{}];
在此,解構繼續如下,而與右手邊是 [{}]
或 []
無關。
const
{
prop
:
x
=
123
}
=
{};
// x = 123
屬性值簡寫是物件文字的一個功能:如果屬性值是與屬性鍵同名的變數,則你可以省略鍵。這也適用於解構
const
{
x
,
y
}
=
{
x
:
11
,
y
:
8
};
// x = 11; y = 8
// Same as:
const
{
x
:
x
,
y
:
y
}
=
{
x
:
11
,
y
:
8
};
你也可以將屬性值簡寫與預設值結合使用
const
{
x
,
y
=
1
}
=
{};
// x = undefined; y = 1
運算屬性鍵是另一個物件文字功能,也適用於解構。如果你將運算式放在方括號中,你可以透過運算式指定屬性的鍵
const
FOO
=
'foo'
;
const
{
[
FOO
]
:
f
}
=
{
foo
:
123
};
// f = 123
運算屬性鍵允許你解構鍵為符號的屬性
// Create and destructure a property whose key is a symbol
const
KEY
=
Symbol
();
const
obj
=
{
[
KEY
]
:
'abc'
};
const
{
[
KEY
]
:
x
}
=
obj
;
// x = 'abc'
// Extract Array.prototype[Symbol.iterator]
const
{
[
Symbol
.
iterator
]
:
func
}
=
[];
console
.
log
(
typeof
func
);
// function
省略讓您使用陣列「洞」的語法,在解構過程中跳過元素
const
[,,
x
,
y
]
=
[
'a'
,
'b'
,
'c'
,
'd'
];
// x = 'c'; y = 'd'
...
) 剩餘運算子讓您將可迭代物件的剩餘元素萃取到陣列中。如果這個運算子用在陣列模式中,它必須放在最後
const
[
x
,
...
y
]
=
[
'a'
,
'b'
,
'c'
];
// x='a'; y=['b', 'c']
如果運算子找不到任何元素,它會將其運算元與空陣列進行比對。也就是說,它永遠不會產生 undefined
或 null
。例如
const
[
x
,
y
,
...
z
]
=
[
'a'
];
// x='a'; y=undefined; z=[]
剩餘運算子的運算元不一定要是變數,您也可以使用模式
const
[
x
,
...[
y
,
z
]]
=
[
'a'
,
'b'
,
'c'
];
// x = 'a'; y = 'b'; z = 'c'
剩餘運算子會觸發下列解構
[
y
,
z
]
=
[
'b'
,
'c'
]
如果您透過解構指定,每個指定目標都可以是正常指定中左側允許的所有內容。
例如,屬性的參考 (obj.prop
)
const
obj
=
{};
({
foo
:
obj
.
prop
}
=
{
foo
:
123
});
console
.
log
(
obj
);
// {prop:123}
或陣列元素的參考 (arr[0]
)
const
arr
=
[];
({
bar
:
arr
[
0
]
}
=
{
bar
:
true
});
console
.
log
(
arr
);
// [true]
您也可以透過剩餘運算子 (...
) 指定給物件屬性和陣列元素。
const
obj
=
{};
[
first
,
...
obj
.
prop
]
=
[
'a'
,
'b'
,
'c'
];
// first = 'a'; obj.prop = ['b', 'c']
如果您透過解構宣告變數或定義參數,則您必須使用簡單識別碼,您不能參考物件屬性和陣列元素。
使用解構時有兩件事要注意
後續兩個區塊包含詳細資訊。
由於程式區塊以大括號開頭,陳述句不能以大括號開頭。在指派中使用物件解構時,這很不幸
{
a
,
b
}
=
someObject
;
// SyntaxError
解決方法是將完整表達式放在括號中
({
a
,
b
}
=
someObject
);
// OK
下列語法無法運作
({
a
,
b
})
=
someObject
;
// SyntaxError
使用 let
、var
和 const
時,大括號永遠不會造成問題
const
{
a
,
b
}
=
someObject
;
// OK
讓我們從幾個較小的範例開始。
for-of
迴圈支援解構
const
map
=
new
Map
().
set
(
false
,
'no'
).
set
(
true
,
'yes'
);
for
(
const
[
key
,
value
]
of
map
)
{
console
.
log
(
key
+
' is '
+
value
);
}
你可以使用解構來交換值。這是引擎可以最佳化的項目,因此不會建立陣列。
[
a
,
b
]
=
[
b
,
a
];
你可以使用解構來分割陣列
const
[
first
,
...
rest
]
=
[
'a'
,
'b'
,
'c'
];
// first = 'a'; rest = ['b', 'c']
一些內建 JavaScript 作業會傳回陣列。解構有助於處理它們
const
[
all
,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
'2999-12-31'
);
如果你只對群組感興趣(而不是完整比對,all
),你可以使用省略號來略過索引 0 的陣列元素
const
[,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
'2999-12-31'
);
如果正規表示式不比對,exec()
會傳回 null
。很不幸地,你無法透過預設值來處理 null
,這就是為什麼你必須在此情況下使用或運算子 (||
)
const
[,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
someStr
)
||
[];
Array.prototype.split()
會傳回陣列。因此,如果你對元素感興趣,而不是陣列,解構會很有用
const
cells
=
'Jane\tDoe\tCTO'
const
[
firstName
,
lastName
,
title
]
=
cells
.
split
(
'\t'
);
console
.
log
(
firstName
,
lastName
,
title
);
解構對於從函式或方法傳回的物件中萃取資料也很有用。例如,迭代器方法 next()
會傳回具有兩個屬性的物件,done
和 value
。下列程式碼透過迭代器 iter
記錄陣列 arr
的所有元素。解構用於 A 行。
const
arr
=
[
'a'
,
'b'
];
const
iter
=
arr
[
Symbol
.
iterator
]();
while
(
true
)
{
const
{
done
,
value
}
=
iter
.
next
();
// (A)
if
(
done
)
break
;
console
.
log
(
value
);
}
陣列解構適用於任何可迭代值。這偶爾很有用
const
[
x
,
y
]
=
new
Set
().
add
(
'a'
).
add
(
'b'
);
// x = 'a'; y = 'b'
const
[
a
,
b
]
=
'foo'
;
// a = 'f'; b = 'o'
要了解多重回傳值的用途,我們實作一個函式 `findElement(a, p)`,用來在陣列 `a` 中尋找第一個函式 `p` 回傳 `true` 的元素。問題是:`findElement()` 應該回傳什麼?有時候我們感興趣的是元素本身,有時候是其索引,有時候是兩者。以下實作回傳兩者。
function
findElement
(
array
,
predicate
)
{
for
(
const
[
index
,
element
]
of
array
.
entries
())
{
// (A)
if
(
predicate
(
element
,
index
,
array
))
{
// We found an element:
return
{
element
,
index
};
// Same as (property value shorthands):
// { element: element, index: index }
}
}
// We couldn’t find anything; return failure values:
return
{
element
:
undefined
,
index
:
-
1
};
}
此函式透過陣列方法 `entries()` 遍歷 `array` 的所有元素,此方法會回傳一個可遍歷的 `[index,element]` 成對資料(第 A 行)。成對資料的部分透過解構存取。
我們來使用 `findElement()`
const
arr
=
[
7
,
8
,
6
];
const
{
element
,
index
}
=
findElement
(
arr
,
x
=>
x
%
2
===
0
);
// element = 8, index = 1
幾個 ECMAScript 6 的功能讓我們可以撰寫更簡潔的程式碼:回呼函式是一個箭頭函式;回傳值透過一個物件模式解構,並使用屬性值簡寫。
由於 `index` 和 `element` 也指屬性金鑰,我們提到它們的順序並不重要。我們可以交換它們,不會有任何改變
const
{
index
,
element
}
=
findElement
(
···
);
我們已經成功處理需要索引和元素的情況。如果我們只對其中一個感興趣呢?事實證明,由於 ECMAScript 6,我們的實作也可以處理這個情況。而且與具有單一回傳值的函式相比,語法開銷很小。
const
a
=
[
7
,
8
,
6
];
const
{
element
}
=
findElement
(
a
,
x
=>
x
%
2
===
0
);
// element = 8
const
{
index
}
=
findElement
(
a
,
x
=>
x
%
2
===
0
);
// index = 1
每次,我們只擷取我們需要的單一屬性的值。
本節從不同的角度來看解構:作為一個遞迴模式比對演算法。
最後,我將使用演算法來解釋以下兩個函式宣告之間的差異。
function
move
({
x
=
0
,
y
=
0
}
=
{})
{
···
}
function
move
({
x
,
y
}
=
{
x
:
0
,
y
:
0
})
{
···
}
解構賦值看起來像這樣
«
pattern
»
=
«
value
»
我們要使用 `pattern` 從 `value` 中擷取資料。我現在將描述一個用於這樣做的演算法,在函式程式設計中稱為 *模式比對*(簡稱:*比對*)。演算法指定運算子 `←`(「比對」)用於解構賦值,它會比對 `pattern` 和 `value`,並在這樣做的同時將其賦值給變數
«
pattern
»
←
«
value
»
演算法透過遞迴規則指定,這些規則會拆解 `←` 運算子的兩個運算元。宣告式表示法可能需要一些時間才能習慣,但它使演算法的指定更簡潔。每個規則都有兩個部分
我們來看一個範例
«
pattern
»
←
obj
.
key
{
«
properties
»
}
←
obj
{} ← obj
(無剩餘屬性)
// Nothing to do
在規則 (2c) 中,標頭表示如果存在至少一個屬性和零個或更多剩餘屬性的物件樣式,則執行此規則。該樣式與值 obj
相符。此規則的效果是執行持續運作,其中屬性值樣式與 obj.key
相符,而剩餘屬性與 obj
相符。
在規則 (2e) 中,標頭表示如果空物件樣式 {}
與值 obj
相符,則執行此規則。然後就沒有任何事要做。
每當呼叫演算法時,都會從上到下檢查規則,並且只執行第一個適用的規則。
我只顯示解構賦值的演算法。解構變數宣告和解構參數定義的工作方式類似。
我也不會涵蓋進階功能(計算屬性金鑰;屬性值速記;物件屬性和陣列元素作為賦值目標)。只會涵蓋基礎知識。
樣式為
x
{«屬性»}
[«元素»]
以下各節描述這三種情況之一。
以下三節說明如何處理這三種情況。每個部分包含一個或多個編號規則。
x ← 值
(包括 undefined
和 null
)
x
=
value
{«屬性»} ← undefined
throw
new
TypeError
();
{«屬性»} ← null
throw
new
TypeError
();
«
pattern
»
←
obj
.
key
{
«
properties
»
}
←
obj
{key: «樣式» = 預設值, «屬性»} ← obj
const
tmp
=
obj
.
key
;
if
(
tmp
!==
undefined
)
{
«
pattern
»
←
tmp
}
else
{
«
pattern
»
←
default_value
}
{
«
properties
»
}
←
obj
{} ← obj
(無剩餘屬性)
// Nothing to do
陣列樣式和可迭代。陣列解構的演算法從陣列樣式和可迭代開始
[«元素»] ← 非可迭代
斷言(!isIterable(非可迭代))
throw
new
TypeError
();
[«元素»] ← 可迭代物件
assert(isIterable(可迭代物件))
const
iterator
=
iterable
[
Symbol
.
iterator
]();
«
elements
»
←
iterator
輔助函式
function
isIterable
(
value
)
{
return
(
value
!==
null
&&
typeof
value
===
'object'
&&
typeof
value
[
Symbol
.
iterator
]
===
'function'
);
}
陣列元素和反覆器。演算法會繼續處理模式的元素(箭號的左側)和從可迭代物件取得的反覆器(箭號的右側)。
«模式», «元素» ← 反覆器
«
pattern
»
←
getNext
(
iterator
)
// undefined after last item
«
elements
»
←
iterator
«模式» = 預設值, «元素» ← 反覆器
const
tmp
=
getNext
(
iterator
);
// undefined after last item
if
(
tmp
!==
undefined
)
{
«
pattern
»
←
tmp
}
else
{
«
pattern
»
←
default_value
}
«
elements
»
←
iterator
, «元素» ← 反覆器
(洞、省略)
getNext
(
iterator
);
// skip
«
elements
»
←
iterator
...«模式» ← 反覆器
(總是最後一部分!)
const
tmp
=
[];
for
(
const
elem
of
iterator
)
{
tmp
.
push
(
elem
);
}
«
pattern
»
←
tmp
← 反覆器
(沒有剩餘元素)
// Nothing to do
輔助函式
function
getNext
(
iterator
)
{
const
{
done
,
value
}
=
iterator
.
next
();
return
(
done
?
undefined
:
value
);
}
在 ECMAScript 6 中,如果呼叫者使用物件文字,而被呼叫者使用解構,你可以模擬命名參數。這個模擬在 參數處理章節 中有詳細說明。以下程式碼顯示一個範例:函式 move1()
有兩個命名參數,x
和 y
function
move1
({
x
=
0
,
y
=
0
}
=
{})
{
// (A)
return
[
x
,
y
];
}
move1
({
x
:
3
,
y
:
8
});
// [3, 8]
move1
({
x
:
3
});
// [3, 0]
move1
({});
// [0, 0]
move1
();
// [0, 0]
A 行中有三個預設值
x
和 y
。move1()
(如最後一行)。但是,為什麼你要在先前的程式碼片段中定義參數?為什麼不如下列方式定義,這也是完全合法的 ES6 程式碼?
function
move2
({
x
,
y
}
=
{
x
:
0
,
y
:
0
})
{
return
[
x
,
y
];
}
為了了解為什麼 move1()
是正確的,我們來使用這兩個函式進行兩個範例。在這樣做之前,讓我們看看如何透過比對來解釋參數的傳遞。
對於函式呼叫,形式參數(在函式定義中)會與實際參數(在函式呼叫中)進行比對。例如,請看以下函式定義和函式呼叫。
function
func
(
a
=
0
,
b
=
0
)
{
···
}
func
(
1
,
2
);
參數 a
和 b
的設定方式與以下解構類似。
[
a
=
0
,
b
=
0
]
←
[
1
,
2
]
move2()
讓我們檢查解構如何對 move2()
運作。
範例 1。move2()
會導致這個解構
[{
x
,
y
}
=
{
x
:
0
,
y
:
0
}]
←
[]
左側的單一陣列元素在右側沒有比對,這就是為什麼 {x,y}
會與預設值進行比對,而不是與右側的資料進行比對(規則 3b、3d)
{
x
,
y
}
←
{
x
:
0
,
y
:
0
}
左邊包含屬性值速記,它是
{
x
:
x
,
y
:
y
}
←
{
x
:
0
,
y
:
0
}
此解構導致以下兩個指定(規則 2c、1)
x
=
0
;
y
=
0
;
範例 2:我們來檢視函式呼叫 move2({z:3})
,它會導致以下解構
[{
x
,
y
}
=
{
x
:
0
,
y
:
0
}]
←
[{
z
:
3
}]
右邊在索引 0 處有一個陣列元素。因此,預設值會被忽略,而下一步是(規則 3d)
{
x
,
y
}
←
{
z
:
3
}
這會導致 x
和 y
都設定為 undefined
,這不是我們想要的。
move1()
我們來試試 move1()
。
範例 1:move1()
[{
x
=
0
,
y
=
0
}
=
{}]
←
[]
右邊在索引 0 處沒有陣列元素,而是使用預設值(規則 3d)
{
x
=
0
,
y
=
0
}
←
{}
左邊包含屬性值速記,這表示此解構等於
{
x
:
x
=
0
,
y
:
y
=
0
}
←
{}
右邊沒有屬性 x
或屬性 y
相符。因此,會使用預設值,並接著執行以下解構(規則 2d)
x
←
0
y
←
0
這會導致以下指定(規則 1)
x
=
0
y
=
0
範例 2:move1({z:3})
[{
x
=
0
,
y
=
0
}
=
{}]
←
[{
z
:
3
}]
陣列樣式的第一個元素在右邊有相符,而相符會用於繼續解構(規則 3d)
{
x
=
0
,
y
=
0
}
←
{
z
:
3
}
如同範例 1,右邊沒有屬性 x
和 y
,而會使用預設值
x
=
0
y
=
0
這些範例說明預設值是樣式部分(物件屬性或陣列元素)的一個功能。如果某個部分沒有相符或與 undefined
相符,則會使用預設值。也就是說,樣式會與預設值相符。