size
屬性,而不是 length
?在 ECMAScript 6 中,以下四個資料結構是新增的:Map
、WeakMap
、Set
和 WeakSet
。
地圖的金鑰可以是任意值
> const map = new Map(); // create an empty Map
> const KEY = {};
> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false
您可以使用包含 [金鑰、值] 配對的陣列(或任何可迭代物件)來設定地圖中的初始資料
const
map
=
new
Map
([
[
1
,
'one'
],
[
2
,
'two'
],
[
3
,
'three'
],
// trailing comma is ignored
]);
集合是唯一元素的集合
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
如您所見,如果將可迭代物件 (範例中的 arr
) 傳遞給建構函式,您便可以使用元素初始化集合。
弱映射是一種映射,不會防止其鍵被垃圾回收。這表示您可以將資料與物件關聯,而不用擔心記憶體外洩。例如
//----- Manage listeners
const
_objToListeners
=
new
WeakMap
();
function
addListener
(
obj
,
listener
)
{
if
(
!
_objToListeners
.
has
(
obj
))
{
_objToListeners
.
set
(
obj
,
new
Set
());
}
_objToListeners
.
get
(
obj
).
add
(
listener
);
}
function
triggerListeners
(
obj
)
{
const
listeners
=
_objToListeners
.
get
(
obj
);
if
(
listeners
)
{
for
(
const
listener
of
listeners
)
{
listener
();
}
}
}
//----- Example: attach listeners to an object
const
obj
=
{};
addListener
(
obj
,
()
=>
console
.
log
(
'hello'
));
addListener
(
obj
,
()
=>
console
.
log
(
'world'
));
//----- Example: trigger listeners
triggerListeners
(
obj
);
// Output:
// hello
// world
JavaScript 一直都有非常精簡的標準函式庫。最遺憾的是缺少用於將值對應到值的資料結構。ECMAScript 5 中最好的方法是透過濫用物件,將字串對應到任意值。即使如此,仍有 幾個陷阱 會讓您跌倒。
ECMAScript 6 中的 Map
資料結構讓您可以使用任意值作為鍵,而且非常受歡迎。
使用單一項目
> const map = new Map();
> map.set('foo', 123);
> map.get('foo')
123
> map.has('foo')
true
> map.delete('foo')
true
> map.has('foo')
false
判斷映射的大小並清除它
> const map = new Map();
> map.set('foo', true);
> map.set('bar', false);
> map.size
2
> map.clear();
> map.size
0
您可以透過可迭代的鍵值「配對」(具有 2 個元素的陣列) 設定映射。一種可能性是使用陣列 (可迭代)
const
map
=
new
Map
([
[
1
,
'one'
],
[
2
,
'two'
],
[
3
,
'three'
],
// trailing comma is ignored
]);
或者,set()
方法是可以串聯的
const
map
=
new
Map
()
.
set
(
1
,
'one'
)
.
set
(
2
,
'two'
)
.
set
(
3
,
'three'
);
任何值都可以是鍵,甚至是物件
const
map
=
new
Map
();
const
KEY1
=
{};
map
.
set
(
KEY1
,
'hello'
);
console
.
log
(
map
.
get
(
KEY1
));
// hello
const
KEY2
=
{};
map
.
set
(
KEY2
,
'world'
);
console
.
log
(
map
.
get
(
KEY2
));
// world
大多數映射操作都需要檢查值是否等於其中一個鍵。它們透過內部操作 SameValueZero 執行此操作,其運作方式類似於 ===
,但將 NaN
視為等於它自己。
讓我們先看看 ===
如何處理 NaN
> NaN === NaN
false
反之,你可以將 NaN
用作 Maps 中的鍵,就像任何其他值一樣
> const map = new Map();
> map.set(NaN, 123);
> map.get(NaN)
123
與 ===
一樣,-0
和 +0
被視為相同的值。這通常是處理兩個零的最佳方式(詳細資訊說明於「Speaking JavaScript」)。
> map.set(-0, 123);
> map.get(+0)
123
不同的物件總是視為不同。這是無法設定(目前)的,如後面的常見問題集中所述。
> new Map().set({}, 1).set({}, 2).size
2
取得未知的鍵會產生 undefined
> new Map().get('asfddfsasadf')
undefined
讓我們設定一個 Map 來示範如何反覆運算它。
const
map
=
new
Map
([
[
false
,
'no'
],
[
true
,
'yes'
],
]);
Maps 記錄元素插入的順序,並在反覆運算鍵、值或項目時遵循該順序。
keys()
會傳回 Map 中鍵的 Iterable
for
(
const
key
of
map
.
keys
())
{
console
.
log
(
key
);
}
// Output:
// false
// true
values()
會傳回 Map 中值的 Iterable
for
(
const
value
of
map
.
values
())
{
console
.
log
(
value
);
}
// Output:
// no
// yes
entries()
會傳回 Map 的項目,作為 [key,value] 成對的 Iterable(陣列)。
for
(
const
entry
of
map
.
entries
())
{
console
.
log
(
entry
[
0
],
entry
[
1
]);
}
// Output:
// false no
// true yes
解構讓你能夠直接存取鍵和值
for
(
const
[
key
,
value
]
of
map
.
entries
())
{
console
.
log
(
key
,
value
);
}
反覆運算 Map 的預設方式是 entries()
> map[Symbol.iterator] === map.entries
true
因此,你可以讓先前的程式碼片段更簡短
for
(
const
[
key
,
value
]
of
map
)
{
console
.
log
(
key
,
value
);
}
展開運算子(...
) 可以將 Iterable 轉換為陣列。這讓我們可以將 Map.prototype.keys()
(一個 Iterable)的結果轉換為陣列
> const map = new Map().set(false, 'no').set(true, 'yes');
> [...map.keys()]
[ false, true ]
Maps 也是 Iterable,這表示展開運算子可以將 Maps 轉換為陣列
> const map = new Map().set(false, 'no').set(true, 'yes');
> [...map]
[ [ false, 'no' ],
[ true, 'yes' ] ]
Map
方法 forEach
具有下列簽章
Map
.
prototype
.
forEach
((
value
,
key
,
map
)
=>
void
,
thisArg
?
)
:
void
第一個參數的簽章反映 Array.prototype.forEach
回呼的簽章,這就是為什麼值會先出現。
const
map
=
new
Map
([
[
false
,
'no'
],
[
true
,
'yes'
],
]);
map
.
forEach
((
value
,
key
)
=>
{
console
.
log
(
key
,
value
);
});
// Output:
// false no
// true yes
你可以對陣列進行 map()
和 filter()
,但 Maps 沒有這些運算。解決方案是
我將使用以下 Map 來示範其運作方式。
const
originalMap
=
new
Map
()
.
set
(
1
,
'a'
)
.
set
(
2
,
'b'
)
.
set
(
3
,
'c'
);
對應 originalMap
const
mappedMap
=
new
Map
(
// step 3
[...
originalMap
]
// step 1
.
map
(([
k
,
v
])
=>
[
k
*
2
,
'_'
+
v
])
// step 2
);
// Resulting Map: {2 => '_a', 4 => '_b', 6 => '_c'}
篩選 originalMap
const
filteredMap
=
new
Map
(
// step 3
[...
originalMap
]
// step 1
.
filter
(([
k
,
v
])
=>
k
<
3
)
// step 2
);
// Resulting Map: {1 => 'a', 2 => 'b'}
步驟 1 由散佈運算子 (...
) 執行,我先前已說明過。
沒有用於合併 Map 的方法,因此必須使用前一節的方法來執行此操作。
讓我們合併以下兩個 Map
const
map1
=
new
Map
()
.
set
(
1
,
'a1'
)
.
set
(
2
,
'b1'
)
.
set
(
3
,
'c1'
);
const
map2
=
new
Map
()
.
set
(
2
,
'b2'
)
.
set
(
3
,
'c2'
)
.
set
(
4
,
'd2'
);
若要合併 map1
和 map2
,我透過散佈運算子 (...
) 將它們轉換成陣列,並串接這些陣列。之後,我將結果轉換回 Map。所有這些都在第一行完成。
> const combinedMap = new Map([...map1, ...map2])
> [...combinedMap] // convert to Array to display
[ [ 1, 'a1' ],
[ 2, 'b2' ],
[ 3, 'c2' ],
[ 4, 'd2' ] ]
如果 Map 包含任意(與 JSON 相容)資料,我們可以透過將其編碼為 key-value 成對(2 元素陣列)的陣列,將其轉換成 JSON。讓我們先探討如何達成此編碼。
散佈運算子可讓您將 Map 轉換成成對陣列
>
const
myMap
=
new
Map
()
.
set
(
true
,
7
)
.
set
(
{
foo
:
3
}
,
[
'abc'
]
);
>
[
...myMap
]
[
[
true
,
7
]
,
[
{
foo
:
3
},
[
'abc'
]
]
]
Map
建構函式可讓您將成對陣列轉換成 Map
> new Map([[true, 7], [{foo: 3}, ['abc']]])
Map {true => 7, Object {foo: 3} => ['abc']}
讓我們使用此知識將任何具有與 JSON 相容資料的 Map 轉換成 JSON,反之亦然
function
mapToJson
(
map
)
{
return
JSON
.
stringify
([...
map
]);
}
function
jsonToMap
(
jsonStr
)
{
return
new
Map
(
JSON
.
parse
(
jsonStr
));
}
以下互動示範如何使用這些函式
>
const
myMap
=
new
Map
()
.
set
(
true
,
7
)
.
set
(
{
foo
:
3
}
,
[
'abc'
]
);
>
mapToJson
(
myMap
)
'
[
[
true
,
7
]
,
[
{
"foo"
:
3
},
[
"abc"
]
]]'
>
jsonToMap
(
'
[
[
true
,
7
]
,
[
{
"foo"
:
3
},
[
"abc"
]
]]'
)
Map
{
true
=>
7,
Object
{
foo
:
3
}
=>
[
'abc'
]
}
只要 Map 僅有字串作為 key,您就可以透過將其編碼為物件,將其轉換成 JSON。讓我們先探討如何達成此編碼。
以下兩個函式將字串 Map 轉換成物件,反之亦然
function
strMapToObj
(
strMap
)
{
const
obj
=
Object
.
create
(
null
);
for
(
const
[
k
,
v
]
of
strMap
)
{
// We don’t escape the key '__proto__'
// which can cause problems on older engines
obj
[
k
]
=
v
;
}
return
obj
;
}
function
objToStrMap
(
obj
)
{
const
strMap
=
new
Map
();
for
(
const
k
of
Object
.
keys
(
obj
))
{
strMap
.
set
(
k
,
obj
[
k
]);
}
return
strMap
;
}
讓我們使用這兩個函式
> const myMap = new Map().set('yes', true).set('no', false);
> strMapToObj(myMap)
{ yes: true, no: false }
> objToStrMap({yes: true, no: false})
[ [ 'yes', true ], [ 'no', false ] ]
使用這些輔助函式,轉換為 JSON 的運作方式如下
function
strMapToJson
(
strMap
)
{
return
JSON
.
stringify
(
strMapToObj
(
strMap
));
}
function
jsonToStrMap
(
jsonStr
)
{
return
objToStrMap
(
JSON
.
parse
(
jsonStr
));
}
這是使用這些函式的範例
> const myMap = new Map().set('yes', true).set('no', false);
> strMapToJson(myMap)
'{"yes":true,"no":false}'
> jsonToStrMap('{"yes":true,"no":false}');
Map {'yes' => true, 'no' => false}
建構函式
new Map(entries? : Iterable<[any,any]>)
iterable
,則會建立一個空的 Map。如果您提供一個 [key, value] 成對的 iterable,則這些成對會用於新增項目至 Map。例如
const
map
=
new
Map
([
[
1
,
'one'
],
[
2
,
'two'
],
[
3
,
'three'
],
// trailing comma is ignored
]);
處理單一項目
Map.prototype.get(key) : any
key
所對應的 value
。如果此 Map 中沒有 key
,則傳回 undefined
。Map.prototype.set(key, value) : this
key
的項目,則會更新該項目。否則,會建立一個新的項目。此方法傳回 this
,表示您可以串連呼叫它。Map.prototype.has(key) : boolean
Map.prototype.delete(key) : boolean
key
的項目,則會移除該項目並傳回 true
。否則,則不會執行任何動作並傳回 false
。處理所有項目
取得 Map.prototype.size : number
Map.prototype.clear() : void
反覆運算和迴圈:會按照項目新增至 Map 的順序進行。
Map.prototype.entries() : Iterable<[any,any]>
Map.prototype.forEach((value, key, collection) => void, thisArg?) : void
thisArg
,則會在每次呼叫時將 this
設定為 thisArg
。否則,會將 this
設定為 undefined
。Map.prototype.keys() : Iterable<any>
Map.prototype.values() : Iterable<any>
Map.prototype[Symbol.iterator]() : Iterable<[any,any]>
Map.prototype.entries
。WeakMaps 的運作方式與 Maps 大致相同,有以下差異
以下各節說明這些差異。
如果將項目新增到 WeakMap,則鍵必須是物件
const
wm
=
new
WeakMap
()
wm
.
set
(
'abc'
,
123
);
// TypeError
wm
.
set
({},
123
);
// OK
WeakMap 中的鍵是弱持有:一般來說,未由任何儲存位置(變數、屬性等)參照的物件可以被垃圾回收。WeakMap 鍵在此意義上不算是儲存位置。換句話說:物件成為 WeakMap 中的鍵不會阻止物件被垃圾回收。
此外,一旦鍵消失,其項目也會消失(最終會消失,但無法偵測何時消失)。
無法檢查 WeakMap 的內部結構,以取得其概觀。這包括無法迭代鍵、值或項目。換句話說:要從 WeakMap 中取得內容,需要一個鍵。也無法清除 WeakMap(作為解決方法,可以建立一個全新的執行個體)。
這些限制啟用了安全性屬性。引用 Mark Miller:「只能由同時擁有 WeakMap 和鍵的人觀察或影響 WeakMap/鍵對應值。如果使用 clear()
,只有 WeakMap 的人就可以影響 WeakMap 和鍵對應值的對應。」
此外,迭代難以實作,因為必須保證鍵保持弱持有。
WeakMaps 對於將資料與無法(或不想)控制其生命週期的物件關聯很有用。在本節中,我們來看兩個範例
透過 WeakMaps,您可以將先前計算的結果與物件關聯,而不用擔心記憶體管理。以下函式 countOwnKeys
就是一個範例:它將先前的結果快取在 WeakMap cache
中。
const
cache
=
new
WeakMap
();
function
countOwnKeys
(
obj
)
{
if
(
cache
.
has
(
obj
))
{
console
.
log
(
'Cached'
);
return
cache
.
get
(
obj
);
}
else
{
console
.
log
(
'Computed'
);
const
count
=
Object
.
keys
(
obj
).
length
;
cache
.
set
(
obj
,
count
);
return
count
;
}
}
如果我們對一個物件 obj
使用這個函式,您會看到只有在第一次呼叫時計算結果,而第二次呼叫則使用快取值
>
const
obj
=
{
foo
:
1
,
bar
:
2
}
;
>
countOwnKeys
(
obj
)
Computed
2
>
countOwnKeys
(
obj
)
Cached
2
假設我們想要附加監聽器到物件,而不改變物件。您可以在物件 obj
中新增監聽器
const
obj
=
{};
addListener
(
obj
,
()
=>
console
.
log
(
'hello'
));
addListener
(
obj
,
()
=>
console
.
log
(
'world'
));
您也可以觸發監聽器
triggerListeners
(
obj
);
// Output:
// hello
// world
兩個函式 addListener()
和 triggerListeners()
可以實作如下。
const
_objToListeners
=
new
WeakMap
();
function
addListener
(
obj
,
listener
)
{
if
(
!
_objToListeners
.
has
(
obj
))
{
_objToListeners
.
set
(
obj
,
new
Set
());
}
_objToListeners
.
get
(
obj
).
add
(
listener
);
}
function
triggerListeners
(
obj
)
{
const
listeners
=
_objToListeners
.
get
(
obj
);
if
(
listeners
)
{
for
(
const
listener
of
listeners
)
{
listener
();
}
}
}
在此使用 WeakMap 的好處是,一旦物件被垃圾回收,其監聽器也會被垃圾回收。換句話說:不會有任何記憶體外洩。
在以下程式碼中,WeakMaps _counter
和 _action
用於儲存 Countdown
實例的虛擬屬性的資料
const
_counter
=
new
WeakMap
();
const
_action
=
new
WeakMap
();
class
Countdown
{
constructor
(
counter
,
action
)
{
_counter
.
set
(
this
,
counter
);
_action
.
set
(
this
,
action
);
}
dec
()
{
let
counter
=
_counter
.
get
(
this
);
if
(
counter
<
1
)
return
;
counter
--
;
_counter
.
set
(
this
,
counter
);
if
(
counter
===
0
)
{
_action
.
get
(
this
)();
}
}
}
有關此技術的更多資訊,請參閱 類別章節。
WeakMap
的建構函式和四個方法與其 Map
等效項的工作方式相同
new
WeakMap
(
entries
?
:
Iterable
<
[
any
,
any
]
>
)
WeakMap
.
prototype
.
get
(
key
)
:
any
WeakMap
.
prototype
.
set
(
key
,
value
)
:
this
WeakMap
.
prototype
.
has
(
key
)
:
boolean
WeakMap
.
prototype
.
delete
(
key
)
:
boolean
ECMAScript 5 也不具備 Set 資料結構。有兩種可能的解決方法
indexOf()
檢查它是否包含元素,透過 filter()
移除元素,等等。這不是一個非常快速的解決方案,但很容易實作。需要注意的一個問題是 indexOf()
找不到值 NaN
。ECMAScript 6 具有資料結構 Set
,它適用於任意值,速度快且正確處理 NaN
。
管理單一元素
> const set = new Set();
> set.add('red')
> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false
確定 Set 的大小並清除它
> const set = new Set();
> set.add('red')
> set.add('green')
> set.size
2
> set.clear();
> set.size
0
您可以透過一個可迭代的 Set 組成元素來設定 Set。例如,透過陣列
const
set
=
new
Set
([
'red'
,
'green'
,
'blue'
]);
或者,add
方法可以串聯
const
set
=
new
Set
().
add
(
'red'
).
add
(
'green'
).
add
(
'blue'
);
new Set()
最多只有一個參數 Set
建構函式有 0 或 1 個參數
其他參數將被忽略,可能會導致意外的結果
> Array.from(new Set(['foo', 'bar']))
[ 'foo', 'bar' ]
> Array.from(new Set('foo', 'bar'))
[ 'f', 'o' ]
對於第二個 Set,只有 'foo'
(可迭代)用於定義 Set。
與 Map 類似,元素的比較方式類似於 ===
,但例外情況是 NaN
與任何其他值都相同。
> const set = new Set([NaN]);
> set.size
1
> set.has(NaN)
true
再次新增元素不會產生任何效果
> const set = new Set();
> set.add('foo');
> set.size
1
> set.add('foo');
> set.size
1
與 ===
類似,兩個不同的物件永遠不會被視為相等(目前無法自訂,稍後在常見問題解答中會說明)
> const set = new Set();
> set.add({});
> set.size
1
> set.add({});
> set.size
2
Set 是可迭代的,而 for-of
迴圈會如你預期般運作
const
set
=
new
Set
([
'red'
,
'green'
,
'blue'
]);
for
(
const
x
of
set
)
{
console
.
log
(
x
);
}
// Output:
// red
// green
// blue
如你所見,Set 保留迭代順序。也就是說,元素總是按照插入順序進行迭代。
先前說明的展開運算子 (...
) 可用於可迭代項目,因此你可以將 Set 轉換為陣列
const
set
=
new
Set
([
'red'
,
'green'
,
'blue'
]);
const
arr
=
[...
set
];
// ['red', 'green', 'blue']
現在我們有簡潔的方法可以將陣列轉換為 Set,反之亦然,這可以消除陣列中的重複項目
const
arr
=
[
3
,
5
,
2
,
2
,
5
,
5
];
const
unique
=
[...
new
Set
(
arr
)];
// [3, 5, 2]
與陣列不同,Set 沒有 map()
和 filter()
方法。解決方法是將它們轉換為陣列,再轉換回來。
對應
const
set
=
new
Set
([
1
,
2
,
3
]);
set
=
new
Set
([...
set
].
map
(
x
=>
x
*
2
));
// Resulting Set: {2, 4, 6}
篩選
const
set
=
new
Set
([
1
,
2
,
3
,
4
,
5
]);
set
=
new
Set
([...
set
].
filter
(
x
=>
(
x
%
2
)
==
0
));
// Resulting Set: {2, 4}
ECMAScript 6 Set 沒有用於計算聯集(例如 addAll
)、交集(例如 retainAll
)或差集(例如 removeAll
)的方法。本節說明如何解決此限制。
聯集(a
∪ b
):建立一個包含 Set a
和 Set b
中元素的 Set。
const
a
=
new
Set
([
1
,
2
,
3
]);
const
b
=
new
Set
([
4
,
3
,
2
]);
const
union
=
new
Set
([...
a
,
...
b
]);
// {1,2,3,4}
模式總是相同
展開運算子 (...
) 會將可迭代項目(例如 Set)的元素插入陣列中。因此,[...a, ...b]
表示將 a
和 b
轉換為陣列並串接。它等於 [...a].concat([...b])
。
交集 (a
∩ b
):建立一個集合,包含集合 a
中也存在於集合 b
中的元素。
const
a
=
new
Set
([
1
,
2
,
3
]);
const
b
=
new
Set
([
4
,
3
,
2
]);
const
intersection
=
new
Set
(
[...
a
].
filter
(
x
=>
b
.
has
(
x
)));
// {2,3}
步驟:將 a
轉換為陣列,篩選元素,將結果轉換為集合。
差集 (a
\ b
):建立一個集合,包含集合 a
中不存在於集合 b
中的元素。此運算有時也稱為減號 (-
)。
const
a
=
new
Set
([
1
,
2
,
3
]);
const
b
=
new
Set
([
4
,
3
,
2
]);
const
difference
=
new
Set
(
[...
a
].
filter
(
x
=>
!
b
.
has
(
x
)));
// {1}
建構函式
new Set(elements? : Iterable<any>)
iterable
,則會建立一個空的集合。如果您有提供,則會將迭代值新增為集合中的元素。例如
const
set
=
new
Set
([
'red'
,
'green'
,
'blue'
]);
單一集合元素
Set.prototype.add(value) : this
value
新增到此集合。此方法會傳回 this
,表示它可以串接。Set.prototype.has(value) : boolean
value
是否在此集合中。Set.prototype.delete(value) : boolean
value
。所有集合元素
get Set.prototype.size : number
Set.prototype.clear() : void
迭代和迴圈
Set.prototype.values() : Iterable<any>
Set.prototype[Symbol.iterator]() : Iterable<any>
Set.prototype.values
。Set.prototype.forEach((value, key, collection) => void, thisArg?)
value
和 key
都設定為該元素,因此此方法的工作方式類似於 Map.prototype.forEach
。如果提供了 thisArg
,則會在每次呼叫時將 this
設定為 thisArg
。否則,this
會設定為 undefined
。與 Map
的對稱性:以下兩個方法的存在只是為了讓集合的介面與映射的介面類似。每個集合元素都處理為一個映射條目,其鍵和值都是該元素。
Set.prototype.entries() : Iterable<[any,any]>
Set.prototype.keys() : Iterable<any>
entries()
允許您將 Set 轉換為 Map
const
set
=
new
Set
([
'a'
,
'b'
,
'c'
]);
const
map
=
new
Map
(
set
.
entries
());
// Map { 'a' => 'a', 'b' => 'b', 'c' => 'c' }
WeakSet
是一個 Set,不會阻止其元素被垃圾回收。請參閱 WeakMap
部分,了解為什麼 WeakSet 不允許迭代、迴圈和清除。
由於您無法迭代其元素,因此 WeakSet 的使用案例並不多。它們確實使您能夠標記物件。
例如,如果您有一個用於代理的工廠函式,則可以使用 WeakSet 來記錄由該工廠建立的物件
const
_proxies
=
new
WeakSet
();
function
createProxy
(
obj
)
{
const
proxy
=
···
;
_proxies
.
add
(
proxy
);
return
proxy
;
}
function
isProxy
(
obj
)
{
return
_proxies
.
has
(
obj
);
}
完整的範例顯示在 代理章節 中。
_proxies
必須是 WeakSet,因為一旦不再參考常規 Set,它就會阻止代理被垃圾回收。
Domenic Denicola 說明 Foo
類別如何確保其方法僅套用於由它建立的執行個體
const
foos
=
new
WeakSet
();
class
Foo
{
constructor
()
{
foos
.
add
(
this
);
}
method
()
{
if
(
!
foos
.
has
(
this
))
{
throw
new
TypeError
(
'Incompatible object!'
);
}
}
}
WeakSet
的建構函式和三個方法與其 Set
等效項的工作方式相同
new
WeakSet
(
elements
?
:
Iterable
<
any
>
)
WeakSet
.
prototype
.
add
(
value
)
WeakSet
.
prototype
.
has
(
value
)
WeakSet
.
prototype
.
delete
(
value
)
size
而不是 length
? 陣列具有屬性 length
來計算條目的數量。Map 和 Set 有不同的屬性 size
。
造成此差異的原因是 length
適用於序列,也就是可編索引的資料結構,例如陣列。size
適用於主要為未排序的集合,例如映射和集合。
如果有一種方法可以設定映射鍵和集合元素的相等條件,這將會很方便。但此功能已被延後,因為要正確且有效率地實作這項功能很困難。
如果您使用鍵從映射中取得資料,偶爾會想要指定一個預設值,以便在映射中找不到鍵時傳回。ES6 映射不允許您直接執行此操作。但您可以使用 Or
運算子 (||
),如下列程式碼所示。countChars
傳回一個映射,將字元對應到出現次數。
function
countChars
(
chars
)
{
const
charCounts
=
new
Map
();
for
(
const
ch
of
chars
)
{
ch
=
ch
.
toLowerCase
();
const
prevCount
=
charCounts
.
get
(
ch
)
||
0
;
// (A)
charCounts
.
set
(
ch
,
prevCount
+
1
);
}
return
charCounts
;
}
在 A 行中,如果 ch
不在 charCounts
中,且 get()
傳回 undefined
,則會使用預設值 0
。
如果您要將字串以外的任何內容對應到任何類型的資料,您別無選擇:您必須使用映射。
然而,如果您要將字串對應到任意資料,您必須決定是否要使用物件。一個粗略的通用準則為
obj.key
map.get(theKey)
映射鍵主要在透過值進行比較時才有意義(相同的「內容」表示兩個值被視為相等,而不是相同的識別碼)。這排除了物件。有一個使用案例:將資料外部附加到物件,但這個使用案例更適合使用弱映射,其中在鍵消失時,條目也會消失。