JavaScript の Object は書きやすく多機能で便利ですが、時折意図しない動作を引き起こします。こういった意図しない動作でよくあるパターンの一つが Object のプロパティを任意キー指定で呼び出す時 constructor や toString といった文字列を入れられて処理が壊れるパターンです(壊れても 1 ユーザのクライアントサイドで閉じていると思いがちですが、データベース等のユーザIDや名前として保存する処理と絡むと広範囲に影響が出ます)。
例えば次の様な処理で想定外の動作になります。
const obj = {// データベース的オブジェクト foo: 'bar', fizz: 'bazz', hoge: 'fuga' } function workAboutObj(key){ if(obj[key]){ // obj について色々処理 } } workAboutObj('foo');// if 内の処理が実行されます workAboutObj('hogehoge');// if 内の処理が実行されません workAboutObj('constructor');// if 内の処理が実行されます // constructor の他にもプロトタイプで繋がっているものは何でも処理のフローに入れてしまいます。
この様な問題を起こさないために JavaScript 組み込み機能の一つである Map が役に立ちます。
Map – JavaScript | MDN
Map は反復処理を行う連想配列として用意されている機能です。使い方は次です。
/* new Map([ ["foo","bar"], ["fizz","bazz"], ["hoge","piyo"] ]) ↑の様に[key, value]の配列を渡すのがMapのコンストラクタの要求です。 ↓ではオブジェクト的に手書きしたいため Object.entries でオブジェクトを配列に変換しています @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/entries */ const map = new Map(Object.entries({ foo: 'bar', // key と value のペアを渡します fizz: 'bazz', hoge: 'fuga' })); const imBar = map.get('foo')// foo に対応する 'bar' を取得 map.set('hoge', 'piyo')// hoge に対応する部分にあった fuga'を'piyo'に置き換え
Object と比較した使い方をしたデモが次です。
この様に Map は Object 同様に連想配列として扱えます。
Map は主に次の二点で Object に勝っています(他の多くの点で Object は Map より便利です。安全と分かっている状況ではガンガン使っていきましょう)。
一つ目は主題ともなっているキーと値の対応の厳密さです。次のコードとデモの様に Map ならば想定している連想配列の外に出ていくことはなく、安全です。
const obj = { foo: 'bar', fizz: 'bazz', hoge: 'fuga' } const map = new Map(Object.entries({...obj})); function workAboutObj(key){ if(obj[key]){ // Object では key === 'constructor' の時、こちらに来る document.getElementById('obj-console').innerText = obj[key]; }else{ document.getElementById('obj-console').innerText = 'これは else 側'; } } function workAboutMap(key){ if(map.get(key)){ document.getElementById('map-console').innerText = map.get(key); }else{ // Map なら key === 'constructor' の時、こちらに来る document.getElementById('map-console').innerText = 'これは else 側'; } } workAboutObj('constructor'); workAboutMap('constructor');
二つ目は素で forEach メソッドを持っていることです。Object では Object.keys や for in からキーを得てオブジェクト本体から値を都度取り出すという手間がかかりましたが Map ではいきなり forEach できます。
const map = new Map(Object.entries({ foo: 'bar', fizz: 'bazz', hoge: 'fuga' })); // Map はループメソッドである forEachを持っています map.forEach((value,key)=>console.log(key,value)) // [key, value]ペアの配列が返ってきて直接使いにくいですが Array.from にも対応しています Array.from(map);// [["foo","bar"],["fizz","bazz"],["hoge","fuga"]]
JSON 変換や”.”一文字での呼び出しなど Object が Map より便利な点はとてもとても多いですが、Object よりも Map の方が適している場面が少なからず存在します。適切に使い分けられるとより早く安全なプログラムをコーディングできます。