sizeプロパティを持ち、lengthプロパティを持たないのか?ECMAScript 6では、Map、WeakMap、Set、WeakSetなど、いくつかの新しいデータ構造が導入されました。
Mapのキーは任意の値にすることができます。
> 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
[キー、値]のペアを含む配列(または任意のイテラブル)を使用して、Mapの初期データを設定できます。
const map = new Map([
[ 1, 'one' ],
[ 2, 'two' ],
[ 3, 'three' ], // trailing comma is ignored
]);
Setは、一意の要素のコレクションです。
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
ご覧のように、コンストラクタにそれらの要素に対するイテラブル(例ではarr)を渡すと、要素でSetを初期化できます。
WeakMapは、キーのガベージコレクションを妨げないMapです。つまり、メモリリークを心配することなく、オブジェクトにデータを関連付けることができます。例えば…
//----- 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で得られる最善の方法は、オブジェクトを悪用することで、文字列から任意の値へのMapです。それでも、いくつかの落とし穴があります。
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
Mapのサイズを決定し、クリアする
> const map = new Map();
> map.set('foo', true);
> map.set('bar', false);
> map.size
2
> map.clear();
> map.size
0
キーと値の「ペア」(2つの要素を持つ配列)に対するイテラブルを介してMapを設定できます。1つの可能性は配列(イテラブル)を使用することです。
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
ほとんどのMap操作では、値がキーのいずれかと等しいかどうかをチェックする必要があります。それらは内部操作SameValueZeroを使用して行われ、===のように動作しますが、NaNはそれ自身と等しいとみなします。
まず、===がNaNをどのように処理するかを見てみましょう。
> NaN === NaN
false
逆に、他の値と同様に、NaNをMapのキーとして使用できます。
> const map = new Map();
> map.set(NaN, 123);
> map.get(NaN)
123
===と同様に、-0と+0は同じ値とみなされます。通常、2つのゼロを処理する最善の方法です(“Speaking JavaScript”で詳細が説明されています)。
> map.set(-0, 123);
> map.get(+0)
123
異なるオブジェクトは常に異なるものとみなされます。これは(まだ)設定できません。後でFAQで説明されているとおりです。
> new Map().set({}, 1).set({}, 2).size
2
不明なキーを取得するとundefinedになります。
> new Map().get('asfddfsasadf')
undefined
Mapの反復処理方法を示すために、Mapを設定してみましょう。
const map = new Map([
[false, 'no'],
[true, 'yes'],
]);
Mapは要素が挿入された順序を記録し、キー、値、またはエントリを反復処理するときにその順序を尊重します。
keys()は、Map内のキーに対するイテラブルを返します。
for (const key of map.keys()) {
console.log(key);
}
// Output:
// false
// true
values()は、Map内の値に対するイテラブルを返します。
for (const value of map.values()) {
console.log(value);
}
// Output:
// no
// yes
entries()は、Mapのエントリを[キー、値]ペア(配列)に対するイテラブルとして返します。
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);
}
スプレッド演算子(...)は、イテラブルを配列に変換できます。これにより、Map.prototype.keys()(イテラブル)の結果を配列に変換できます。
> const map = new Map().set(false, 'no').set(true, 'yes');
> [...map.keys()]
[ false, true ]
Mapもイテラブルであるため、スプレッド演算子を使用してMapを配列に変換できます。
> 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()できますが、Mapにはそのような操作はありません。解決策は次のとおりです。
これがどのように機能するかを示すために、次の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を結合するためのメソッドがないため、前のセクションの方法を使用する必要があります。
次の2つの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互換の)データが含まれている場合、キーと値のペア(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にキーとして文字列のみが含まれている場合は、オブジェクトとしてエンコードすることでJSONに変換できます。まず、そのエンコード方法を調べましょう。
次の2つの関数は、文字列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;
}
これらの2つの関数を使用してみましょう。
> 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が作成されます。[キー、値]ペアに対するイテラブルを提供する場合、それらのペアを使用してMapにエントリが追加されます。例: const map = new Map([
[ 1, 'one' ],
[ 2, 'two' ],
[ 3, 'three' ], // trailing comma is ignored
]);
単一のエントリの処理
Map.prototype.get(key) : anykeyがマップされているvalueを返します。このMapにキーkeyがない場合、undefinedが返されます。Map.prototype.set(key, value) : thiskeyであるエントリが既に存在する場合は、更新されます。それ以外の場合は、新しいエントリが作成されます。このメソッドはthisを返すため、チェーンできます。Map.prototype.has(key) : booleanMap.prototype.delete(key) : booleankeyであるエントリがある場合、削除され、trueが返されます。それ以外の場合は、何も起こらず、falseが返されます。すべてエントリの処理
get Map.prototype.size : numberMap.prototype.clear() : void**反復処理とループ:**エントリがMapに追加された順序で行われます。
Map.prototype.entries() : Iterable<[any,any]>Map.prototype.forEach((value, key, collection) => void, thisArg?) : voidthisArgが指定されている場合、各呼び出しにおいて`this`はそれに設定されます。それ以外の場合は、thisはundefinedに設定されます。Map.prototype.keys() : Iterable<any>Map.prototype.values() : Iterable<any>Map.prototype[Symbol.iterator]() : Iterable<[any,any]>Map.prototype.entriesを参照します。WeakMapは、主にMapと同様に機能しますが、以下の違いがあります。
以降のセクションでは、これらの違いについてそれぞれ説明します。
WeakMapにエントリを追加する場合、キーはオブジェクトでなければなりません。
const wm = new WeakMap()
wm.set('abc', 123); // TypeError
wm.set({}, 123); // OK
WeakMapのキーは弱参照されます。通常、いかなる記憶領域(変数、プロパティなど)からも参照されていないオブジェクトは、ガベージコレクションの対象となります。WeakMapのキーは、そのような意味での記憶領域とは見なされません。言い換えると、WeakMapのキーであるオブジェクトは、そのオブジェクトがガベージコレクションされるのを防ぎません。
さらに、キーがなくなると、そのエントリも(最終的には)消えます(ただし、いつ消えるかを検出する方法はありません)。
WeakMapの中身を検査したり、概要を取得したりすることはできません。これには、キー、値、またはエントリを反復処理できないことも含まれます。言い換えると、WeakMapからコンテンツを取得するには、キーが必要です。WeakMapをクリアする方法もありません(回避策として、まったく新しいインスタンスを作成できます)。
これらの制限により、セキュリティ特性が有効になります。Mark Millerの言葉を引用すると、「weakmap/keyペアの値からのマッピングは、weakmapとキーの両方を持っている人だけが観測または変更できます。clear()を使用すると、WeakMapしか持っていない人がWeakMapとキーから値へのマッピングに影響を与えることができてしまいます。」
さらに、キーが弱参照された状態を保証する必要があるため、反復処理の実装は困難になります。
WeakMapは、ライフサイクルを制御できない(または制御したくない)オブジェクトにデータを関連付けるのに役立ちます。このセクションでは、2つの例を見ていきます。
WeakMapを使用すると、メモリ管理を心配することなく、事前に計算された結果をオブジェクトに関連付けることができます。次の関数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で使用すると、結果は最初の呼び出しに対してのみ計算され、2回目の呼び出しではキャッシュされた値が使用されることがわかります。
> 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
2つの関数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を使用する利点は、オブジェクトがガベージコレクションされると、そのリスナーもガベージコレクションされることです。言い換えると、メモリリークは発生しません。
次のコードでは、WeakMap _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のコンストラクタと4つのメソッドは、対応する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データ構造はありません。2つの回避策があります。
indexOf()を使用して要素が含まれているかどうかを確認し、filter()を使用して要素を削除するなどします。これは非常に高速なソリューションではありませんが、実装は容易です。注意すべき点として、indexOf()は値NaNを見つけることができません。ECMAScript 6には、任意の値に対して機能し、高速でNaNを正しく処理するSetデータ構造があります。
単一要素の管理
> 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()は最大で1つの引数です Setコンストラクタは、0個または1個の引数を取ります。
それ以降の引数は無視されるため、予期しない結果につながる可能性があります。
> Array.from(new Set(['foo', 'bar']))
[ 'foo', 'bar' ]
> Array.from(new Set('foo', 'bar'))
[ 'f', 'o' ]
2番目のSetでは、'foo'(イテラブル)のみを使用してSetが定義されます。
Mapと同様に、要素は===と同様に比較されますが、NaNは他の値と同じように扱われます。
> const set = new Set([NaN]);
> set.size
1
> set.has(NaN)
true
要素を2回目に追加しても、効果はありません。
> const set = new Set();
> set.add('foo');
> set.size
1
> set.add('foo');
> set.size
1
===と同様に、2つの異なるオブジェクトは決して等しいとは見なされません(現在カスタマイズできません。FAQで後述)。
> 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):Set bにも含まれるSet aの要素を含むSetを作成します。
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を配列に変換し、要素をフィルタリングし、結果をSetに変換します。
差集合(a \ b):Set bに含まれていないSet aの要素を含むSetを作成します。この操作は、マイナス(-)と呼ばれることもあります。
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パラメータを指定しない場合、空のSetが作成されます。指定した場合、イテレートされた値がSetの要素として追加されます。例: const set = new Set(['red', 'green', 'blue']);
単一Set要素
Set.prototype.add(value) : thisvalueを追加します。このメソッドはthisを返すため、チェーン可能です。Set.prototype.has(value) : booleanvalueがこのSetに含まれているかどうかを確認します。Set.prototype.delete(value) : booleanvalueを削除します。すべてのSet要素
get Set.prototype.size : numberSet.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はそれに設定されます。それ以外の場合は、thisはundefinedに設定されます。Mapとの対称性: 以下の2つのメソッドは、SetのインターフェースをMapのインターフェースと似せるためだけに存在します。各Set要素は、キーと値が要素である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です。WeakSetがイテレーション、ループ、クリアを許可しない理由については、WeakMapに関するセクションを参照してください。
要素を反復処理できないため、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のコンストラクタと3つのメソッドは、対応する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が主に順序付けられていないコレクション(MapとSetのような)用であるためです。
MapのキーとSetの要素の等価性を構成する方法があれば便利ですが、その機能は適切かつ効率的に実装するのが困難なため、延期されています。
キーを使用してMapから何かを取得する場合、キーがMapにない場合に返されるデフォルト値を指定したいことがあります。ES6のMapでは、これを直接行うことはできません。しかし、次のコードに示すように、Or演算子(||)を使用できます。countCharsは、文字を出現回数にマッピングするMapを返します。
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が使用されます。
文字列以外のものをあらゆる種類のデータにマッピングする場合は、選択肢がありません。Mapを使用する必要があります。
ただし、文字列を任意のデータにマッピングする場合は、オブジェクトを使用するかどうかを決定する必要があります。大まかな一般的なガイドラインは次のとおりです。
obj.keymap.get(theKey)Mapのキーは、主に値で比較する場合に意味があります(同じ「内容」は、同じ同一性ではなく、2つの値が等しいと見なされることを意味します)。これにはオブジェクトは含まれません。1つのユースケースは、オブジェクトに外部からデータを添付することですが、このユースケースは、キーが消えるとエントリも消えるWeakMapの方が適しています。