戦えプログラマン

いつだって戦いなのだ

JavaScript で Map オブジェクトを使うのはやめよう

tl;dr

理由

  • copy() や JSON.stringify() した時の結果が環境によって異なる。
  • debuggability が低い。

再現コード

const pairs = new Map();
pairs.set("k1", "v1");
pairs.set("k2", "v2");
console.log(pairs);
console.log(JSON.stringify(pairs));

node 環境

$ node -v
v11.11.0
Map { 'k1' => 'v1', 'k2' => 'v2' }
{}

恐らく何らかの Polyfill(*1)を含む環境

Map(2) {"k1" => "v1", "k2" => "v2"}
[["k1", "v1"], ["k2", "v2"]]
  • 例えば、 https://hatenablog.com/Chrome にて開き、DevTools の Console で実行してみてほしい。
  • 上記のような結果になるはず。

何が問題か

  • ターゲットに IE を含んでいたり、レガシーなフロントエンドだと、何らかの Polyfill を含んでいることが多い。
  • そんな環境で謎の Object 変数を調べるために、「Chrome の debugger + console で copy() して、VSCode に貼って、JSON 整形して、中身を読む」なんてことをやってると、見事ハマる。
  • 「ある環境では空 Object、ある環境では変な配列、でも実際に動作してるのは Map 型」などという挙動をするのだ。
  • 悪夢である。

Map でしか実現できない操作があるのでは?

  • 思いついたのは、 Object の key に文字列以外(オブジェクトや配列)を使いたい場合だろうか?
const m1 = new Map([[{ x: 1 }, 11], [{ x: 2 }, 22]]);
m1.get({ x: 1 });
undefined;
const m2 = new Map([[[0, 1], 11], [[0, 2], 22]]);
m2.get([0, 2]);
undefined;

・・・Map でもダメやんけ!

ESLint で Map オブジェクトを禁止する

  • ぶっちゃけ使い道ないし使って欲しくないので、禁止してしまおう。
  • .eslintrc.json / .eslintrc.js に下記ルールを書く。
{
  // ...略...
  "rules": {
    "no-restricted-globals": [
      "error",
      {
        "name": "Map",
        "message": "Use standard object."
      }
    ]
  }
}
/some-project/src/index.js
    32:17  error  Unexpected use of 'Map'. Use standard object.       no-restricted-globals

補足

恐らく何らかの Polyfill(*1)

  • 状況からの推察でしかないため、こんなあやふやな書き方をしている。
  • (少なくとも自分は)「Map を使う理由がない」と判断した時点で調べる気がなくなってしまった。すまない。