19. マップとセット
目次
本書をサポートしてください:購入 (PDF, EPUB, MOBI) または 寄付
(広告、ブロックしないでください。)

19. マップとセット



19.1 概要

ECMAScript 6では、MapWeakMapSetWeakSetなど、いくつかの新しいデータ構造が導入されました。

19.1.1 マップ

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
]);

19.1.2 セット

Setは、一意の要素のコレクションです。

const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]

ご覧のように、コンストラクタにそれらの要素に対するイテラブル(例ではarr)を渡すと、要素でSetを初期化できます。

19.1.3 WeakMap

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

19.2 Map

JavaScriptは常に非常に簡素な標準ライブラリを持っていました。値を値にマッピングするためのデータ構造がひどく不足していました。ECMAScript 5で得られる最善の方法は、オブジェクトを悪用することで、文字列から任意の値へのMapです。それでも、いくつかの落とし穴があります。

ECMAScript 6のMapデータ構造を使用すると、任意の値をキーとして使用できるようになり、非常に歓迎されます。

19.2.1 基本操作

単一のエントリを操作する

> 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

19.2.2 Mapの設定

キーと値の「ペア」(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');

19.2.3 キー

オブジェクトも含め、任意の値をキーにすることができます。

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
19.2.3.1 どのキーが等しいとみなされるか?

ほとんどの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

19.2.4 Mapの反復処理

Mapの反復処理方法を示すために、Mapを設定してみましょう。

const map = new Map([
    [false, 'no'],
    [true,  'yes'],
]);

Mapは要素が挿入された順序を記録し、キー、値、またはエントリを反復処理するときにその順序を尊重します。

19.2.4.1 キーと値のイテラブル

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
19.2.4.2 エントリのイテラブル

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);
}
19.2.4.3 イテラブル(Mapを含む)を配列に変換する

スプレッド演算子(...は、イテラブルを配列に変換できます。これにより、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' ] ]

19.2.5 Mapのエントリのループ処理

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

19.2.6 Mapのマッピングとフィルタリング

配列をmap()およびfilter()できますが、Mapにはそのような操作はありません。解決策は次のとおりです。

  1. Mapを[キー、値]ペアの配列に変換します。
  2. 配列をマップまたはフィルタリングします。
  3. 結果を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は、以前に説明したスプレッド演算子(...)によって実行されます。

19.2.7 Mapの結合

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');

map1map2を結合するには、スプレッド演算子(...)を使用して配列に変換し、それらの配列を連結します。その後、結果をMapに戻します。これらすべてが最初の行で行われます。

> const combinedMap = new Map([...map1, ...map2])
> [...combinedMap] // convert to Array to display
[ [ 1, 'a1' ],
  [ 2, 'b2' ],
  [ 3, 'c2' ],
  [ 4, 'd2' ] ]

19.2.8 任意のMapのJSON表現(ペアの配列を使用)

Mapに任意の(JSON互換の)データが含まれている場合、キーと値のペア(2要素の配列)の配列としてエンコードすることで、JSONに変換できます。まず、そのエンコード方法を調べましょう。

19.2.8.1 ペアの配列との間のMapの変換

スプレッド演算子を使用すると、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']}
19.2.8.2 JSONとの間の変換

この知識を使用して、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']}

19.2.9 文字列MapのJSON表現(オブジェクトを使用)

Mapにキーとして文字列のみが含まれている場合は、オブジェクトとしてエンコードすることでJSONに変換できます。まず、そのエンコード方法を調べましょう。

19.2.9.1 オブジェクトとの間の文字列Mapの変換

次の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 ] ]
19.2.9.2 JSONとの間の変換

これらのヘルパー関数を使用すると、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}

19.2.10 Map API

コンストラクタ

単一のエントリの処理

すべてエントリの処理

**反復処理とループ:**エントリがMapに追加された順序で行われます。

19.3 WeakMap

WeakMapは、主にMapと同様に機能しますが、以下の違いがあります。

以降のセクションでは、これらの違いについてそれぞれ説明します。

19.3.1 WeakMapのキーはオブジェクトです

WeakMapにエントリを追加する場合、キーはオブジェクトでなければなりません。

const wm = new WeakMap()

wm.set('abc', 123); // TypeError
wm.set({}, 123); // OK

19.3.2 WeakMapのキーは弱参照されます

WeakMapのキーは弱参照されます。通常、いかなる記憶領域(変数、プロパティなど)からも参照されていないオブジェクトは、ガベージコレクションの対象となります。WeakMapのキーは、そのような意味での記憶領域とは見なされません。言い換えると、WeakMapのキーであるオブジェクトは、そのオブジェクトがガベージコレクションされるのを防ぎません。

さらに、キーがなくなると、そのエントリも(最終的には)消えます(ただし、いつ消えるかを検出する方法はありません)。

19.3.3 WeakMapの概要を取得したり、クリアしたりすることはできません

WeakMapの中身を検査したり、概要を取得したりすることはできません。これには、キー、値、またはエントリを反復処理できないことも含まれます。言い換えると、WeakMapからコンテンツを取得するには、キーが必要です。WeakMapをクリアする方法もありません(回避策として、まったく新しいインスタンスを作成できます)。

これらの制限により、セキュリティ特性が有効になります。Mark Millerの言葉を引用すると、「weakmap/keyペアの値からのマッピングは、weakmapとキーの両方を持っている人だけが観測または変更できます。clear()を使用すると、WeakMapしか持っていない人がWeakMapとキーから値へのマッピングに影響を与えることができてしまいます。」

さらに、キーが弱参照された状態を保証する必要があるため、反復処理の実装は困難になります。

19.3.4 WeakMapのユースケース

WeakMapは、ライフサイクルを制御できない(または制御したくない)オブジェクトにデータを関連付けるのに役立ちます。このセクションでは、2つの例を見ていきます。

19.3.4.1 WeakMapを使用した計算結果のキャッシング

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
19.3.4.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を使用する利点は、オブジェクトがガベージコレクションされると、そのリスナーもガベージコレクションされることです。言い換えると、メモリリークは発生しません。

19.3.4.3 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)();
        }
    }
}

この手法の詳細については、クラスに関する章を参照してください。

19.3.5 WeakMap API

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

19.4 Set

ECMAScript 5にもSetデータ構造はありません。2つの回避策があります。

ECMAScript 6には、任意の値に対して機能し、高速でNaNを正しく処理するSetデータ構造があります。

19.4.1 基本操作

単一要素の管理

> 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

19.4.2 Setの設定

Setを構成する要素を反復処理するイテラブル(たとえば、配列)を使用して、Setを設定できます。

const set = new Set(['red', 'green', 'blue']);

または、addメソッドはチェーン可能です。

const set = new Set().add('red').add('green').add('blue');
19.4.2.1 注意点: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が定義されます。

19.4.3 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

19.4.4 反復処理

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]

19.4.5 マッピングとフィルタリング

配列とは異なり、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}

19.4.6 和集合、積集合、差集合

ECMAScript 6のSetには、和集合(例:addAll)、積集合(例:retainAll)、差集合(例:removeAll)を計算するためのメソッドがありません。このセクションでは、その制限を回避する方法について説明します。

19.4.6.1 和集合

和集合(ab):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]は、abが配列に変換され、連結されることを意味します。これは[...a].concat([...b])と同等です。

19.4.6.2 積集合

積集合(ab):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に変換します。

19.4.6.3 差集合

差集合(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}

19.4.7 Set API

コンストラクタ

単一Set要素

すべてのSet要素

反復処理とループ

Mapとの対称性: 以下の2つのメソッドは、SetのインターフェースをMapのインターフェースと似せるためだけに存在します。各Set要素は、キーと値が要素であるMapエントリとして扱われます。

entries()を使用すると、SetをMapに変換できます。

const set = new Set(['a', 'b', 'c']);
const map = new Map(set.entries());
    // Map { 'a' => 'a', 'b' => 'b', 'c' => 'c' }

19.5 WeakSet

WeakSetは、要素のガベージコレクションを妨げないSetです。WeakSetがイテレーション、ループ、クリアを許可しない理由については、WeakMapに関するセクションを参照してください。

19.5.1 WeakSetのユースケース

要素を反復処理できないため、WeakSetのユースケースはそれほど多くありません。オブジェクトにマークを付けることができます。

19.5.1.1 ファクトリ関数によって作成されたオブジェクトのマーク付け

たとえば、プロキシのファクトリ関数がある場合、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を使用すると、プロキシが参照されなくなってもガベージコレクションが妨げられるためです。

19.5.1.2 メソッドで使用できるオブジェクトのマーク付け

Domenic Denicolaは、クラスFooが、そのメソッドを自身によって作成されたインスタンスのみに適用する方法を示しています。

const foos = new WeakSet();

class Foo {
    constructor() {
        foos.add(this);
    }

    method() {
        if (!foos.has(this)) {
            throw new TypeError('Incompatible object!');
        }
    }
}

19.5.2 WeakSet API

WeakSetのコンストラクタと3つのメソッドは、対応するSetのものと同じように動作します。

new WeakSet(elements? : Iterable<any>)

WeakSet.prototype.add(value)
WeakSet.prototype.has(value)
WeakSet.prototype.delete(value)

19.6 FAQ: MapとSet

19.6.1 MapとSetはなぜsizeプロパティを持ち、lengthプロパティを持たないのですか?

配列はエントリの数を数えるためにlengthプロパティを持ちます。MapとSetは異なるプロパティsizeを持ちます。

この違いの理由は、lengthがシーケンス(配列のようにインデックス可能なデータ構造)用であり、sizeが主に順序付けられていないコレクション(MapとSetのような)用であるためです。

19.6.2 MapとSetのキーと値の比較方法を構成することはできませんか?

MapのキーとSetの要素の等価性を構成する方法があれば便利ですが、その機能は適切かつ効率的に実装するのが困難なため、延期されています。

19.6.3 Mapから何かを取得するときにデフォルト値を指定する方法はありますか?

キーを使用して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行では、chcharCountsにない場合、get()undefinedを返すため、デフォルトの0が使用されます。

19.6.4 Mapをいつ使用し、オブジェクトをいつ使用すべきですか?

文字列以外のものをあらゆる種類のデータにマッピングする場合は、選択肢がありません。Mapを使用する必要があります。

ただし、文字列を任意のデータにマッピングする場合は、オブジェクトを使用するかどうかを決定する必要があります。大まかな一般的なガイドラインは次のとおりです。

19.6.5 Mapでオブジェクトをキーとして使用するのはどのような場合ですか?

Mapのキーは、主に値で比較する場合に意味があります(同じ「内容」は、同じ同一性ではなく、2つの値が等しいと見なされることを意味します)。これにはオブジェクトは含まれません。1つのユースケースは、オブジェクトに外部からデータを添付することですが、このユースケースは、キーが消えるとエントリも消えるWeakMapの方が適しています。

次: 20. 型付き配列