【JavaScript】Map/Set① 〜Map〜

# 概要
JavaScriptを学習、理解を深めるため「[JavaScript Primer 迷わないための入門書](https://jsprimer.net/)」を読み、
理解した内容等を記載していく。

「[【JavaScript】JavaScript入門一覧](https://qiita.com/andota05/items/22fae452225b785fd405)」に他の記事をまとめています。

# この記事で理解できること
* Map、WeakMapオブジェクトについて
* Map、WeakMapオブジェクトで使用できるメソッドについて

# Map
* マップ型のコレクションを扱うためのビルトインオブジェクト。
* マップが持つ要素について挿入順で反復処理を行うことができる。

> **マップとは**
> キーと値の組み合わせからなる抽象データ型のこと。
> ※他のプログラミング言語の文脈では辞書やハッシュマップ、連想配列とも呼ばれる

# マップの作成と初期化
* Mapオブジェクトをnewすることで、新しいマップを作成できる。
* コンストラクタの初期値として、`エントリーの配列`を渡すことができる。

> **エントリーとは**
> 1つのキーと値の組み合わせを[キー, 値]という形式の配列で表現したもの。

```js
const map = new Map();
// 作成した時点では何も持っていない
console.log(map.size); // => 0
```
```js
// エントリーを定義
const entryNumbers = [["One", 1], ["Two", 2], ["Three", 3]];

const map = new Map(entryNumbers);
// 初期化後のサイズも反映されている
console.log(map.size); // => 3
```

# 値の追加と取り出し
以下のようなマップの要素を扱うメソッドが存在する。

|メソッド|概要|構文|返り値|
|--|--|--|--|
|set|マップに新しい要素を追加|myMap.set(key, value)|(要素が追加された)Mapオブジェクト|
|get|マップが持つ特定のキーに紐づいた値を取り出す|myMap.get(key)|指定されたキーに関連付けられた要素|
|has|マップが指定したvalueに紐づく要素を持っているか確認|myMap.has(value)|存在している場合「true」、存在しない場合「false」|
|delete|マップから指定したキーに紐づいた要素を削除|myMap.delete(value)|要素を削除できた場合「true」、指定した要素が存在しない場合「false」|
|clear|マップが持つすべての要素を削除|myMap.clear()|undefined|

```js
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);
// 初期値のサイズ
console.log(myMap.size);              // => 2

// 新しい要素の追加
myMap.set("key3", "value3");
console.log(myMap.size);              // => 3

// 特定のキーにひもづいた値を取り出す
console.log(myMap.get("key3"));       // => value3

// 指定したキーに紐づく要素を持っているか確認
console.log(myMap.has("key3"));       // => true
console.log(myMap.has("key4"));       // => false

// 指定したキーに紐づく要素を削除
console.log(myMap.delete("key3"));    // => true
console.log(myMap.delete("key3"));    // => false

// マップが持つすべての要素を削除
console.log(myMap.clear());           // => undefined

console.log(myMap.size);              // => 0
```

# マップの反復処理
* マップが持つ要素を反復処理するには、`forEach`、`keys`、`values`、`entries`メソッドを使用する。
* また、Mapオブジェクト自身も`iterable`なオブジェクトであるため`for...of文`で反復処理が可能。

```js
// Mapオブジェクト自身を反復処理
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);

// Mapオブジェクトをfor..ofで反復処理
for (const map of myMap) {
  console.log(map);
}
// => ['key1', 'value1']
// => ['key1', 'value1']
```

## forEachメソッド
* forEachメソッドではマップが持つすべての要素を、`マップへの挿入順`に反復処理が可能。

```js
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);

// forEachでループ処理(コールバック関数の引数には値、キー、マップが渡される)
myMap.forEach((value, key, map) => {
  console.log(`${key}: ${value}`);
});
// => key1: value1
// => key2: value2
```

## keysメソッド
* keysメソッドはマップが持つすべての要素のキーを挿入順に並べたIteratorオブジェクトを返す。

```js
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);

for (const key of myMap.keys()) {
  console.log(key);
}
// => key1
// => key2
```

## valuesメソッド
* valuesメソッドはマップが持つすべての要素の値を挿入順に並べたIteratorオブジェクトを返す。

```js
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);

for (const value of myMap.values()) {
  console.log(value);
}
// => value1
// => value2
```

## entriesメソッド
* entriesメソッドはマップが持つすべての要素をエントリーとして挿入順に並べたIteratorオブジェクトを返す。
* エントリーは`[キー, 値]`のような配列のため、配列の`分割代入`を使うとエントリーからキーと値を簡単に取り出せる。

```js
const myMap = new Map([["key1", "value1"], ["key2", "value2"]]);

for (const [key, value] of myMap.entries()) {
  console.log(`${key}:${value}`);
}
// => key1:value1
// => key2:value2
```

# WeakMap
* `Map`と同様に`マップ`を扱うためのビルトインオブジェクト。
* Mapと違う点は、キーを[弱い参照（Weak Reference）](https://ja.wikipedia.org/wiki/%E5%BC%B1%E3%81%84%E5%8F%82%E7%85%A7)で持つこと。
* キーは`オブジェクト`でなければならない。
* `WeakMap`内に格納されているオブジェクトへの参照が他にない場合、ガベージコレクションによりメモリ領域が解放される。

以下のようなマップの要素を扱うメソッドが存在する。(ここでのマップとはWeakMapのこと)

|メソッド|概要|構文|返り値|
|--|--|--|--|
|set|マップに新しい要素を追加|myWeakMap.set(オブジェクト, value)|(要素が追加された)Mapオブジェクト|
|has|マップが指定したキーに紐づく要素を持っているか確認|myWeakMap.has(key)|存在している場合「true」、存在しない場合「false」|
|delete|マップから指定したキーに紐づいた要素を削除|myWeakMap.delete(key)|要素を削除できた場合「true」、指定した要素が存在しない場合「false」|

```js
const obj1 = {};
const obj2 = {};
const obj3 = {};

const myWeakMap = new WeakMap();

// 新しい要素の追加
myWeakMap.set(obj1, "value1");
myWeakMap.set(obj2, "value2");

// 指定したキーに紐づく要素を持っているか確認
console.log(myWeakMap.has(obj1));     // => true
console.log(myWeakMap.has({}));       // => false

// 指定したキーに紐づく要素を削除
console.log(myWeakMap.delete(obj2));  // => true
console.log(myWeakMap.delete(obj3));  // => false
```