JavaScriptでなぜ Object.create(null) を使うのか？

# 概要

JavaScriptの `Object.create(null)` はなぜ使われるのか。
`{}`での初期化とは何が違うのか。


# きっかけ

[Vuex](https://vuex.vuejs.org/en/index.html) のソースコードを読んでいて、  `Object.create(null)` という見慣れない[記述](https://github.com/vuejs/vuex/blob/dev/src/index.js#L22)を見つけました。
リテラル `{}` ではいけない理由が気になったので、なぜなのか調べてみました。 
 

![スクリーンショット 2016-11-03 午後2.25.52.png](https://qiita-image-store.s3.amazonaws.com/0/10272/77a34e9f-6a79-da35-e680-67baa685fc06.png)


# 対応ブラウザ

`Object.create` は Internet Explorer 9 を含めたモダンブラウザで利用可能です。
　
- http://kangax.github.io/compat-table/es5/#test-Object.create
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create


# Object.create() と {} の違い

[MDN - Object.create()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) によると、 `Object.create` は `prototype` を指定してオブジェクトを生成する ECMAScript 5 からの仕様のようです。

```js
var a = {}
var b = Object.create(Object.prototype)
```

この `a` と `b` は実質同じ挙動になります。


# パフォーマンス？

よくある理由として考えたのはパフォーマンスの理由が挙げられます。

ところが調べてみると、リテラルでの生成の方が数倍高速に動作するようです。

https://jsperf.com/create-object-object-create-null-vs


<i>※ 2016-11-07追記: リンクを差し替え</i>

![スクリーンショット 2016-11-07 午後1.35.19.png](https://qiita-image-store.s3.amazonaws.com/0/10272/b79ef165-e2ad-a8e9-c68f-d4b6e4078f48.png)



# ハッシュとしてのオブジェクトの利用

JavaScriptでオブジェクト使うシーンとして多いのが、ハッシュとしてキャッシュやデータ保存目的で使われるシーンですよね。
気軽に「key-value形式」で保存できるので様々な箇所で使われますが、ユーザーの入力値をキーとして使う場合に問題が出てきます。

例えば、`users`というオブジェクトをハッシュのように利用して、ユーザーの入力値（この例では `account`と`name`の値のセット）を保存していく場合を考えてみます。

```js
var users = {} // { アカウント名: 名前 }

function addUser(account, name) {
  // キーが既に登録されていなければ保存
  if (!users[account]) {
    users[account] = name
  }
}

addUser('taro', '山田太郎')
addUser('taro', '田中太郎') // 保存されない
```

`taro` というキーははじめて設定するので、値`山田太郎`は無事に保存できます。
二度目に `taro` というキーで保存使用すると、既に存在するので無視されます。


では、アカウント名として `constructor` という厳つい単語を指定をした場合にどうなるでしょうか？

```js
addUser('constructor', '鈴木次郎') // 保存されない
```

この場合、 `users.prototype` が`constructor`というメソッドを持っているので、登録できないことになってしまいます。

他にも、 `toString` や `valueOf` なども利用できません。


これは、 `var users = {}` の時点で、 `Object.prototype`を引き継いでいるからとなります。

# 本当に空のオブジェクト

ここで、 `Object.create(null)` の出番です。


```js
var users = Object.create(null) // { アカウント名: 名前 }

function addUser(account, name) {
  // キーが既に登録されていなければ保存
  if (!users[account]) {
    users[account] = name
  }
}

addUser('taro', '山田太郎')
addUser('constructor', '鈴木次郎') // 保存できる
```

このように、オブジェクトを `Object.create(null)` で初期化することで、 `Object.prototype` を引き継がない「本当に空の」ハッシュとして利用できるようになります。


この仕組みは、ユーザー入力値をキーとして使う場合だけでなく、ライブラリとしてAPIを公開している場合にも活用できます。

冒頭のVuexで`Object.create(null)`が使われていたのは、ライブラリのAPIとして、ユーザーが自由な名称でメソッドを追加できる仕組みがあるからですね。(actionやmutationなど)


![スクリーンショット 2016-11-03 午後3.21.11.png](https://qiita-image-store.s3.amazonaws.com/0/10272/f37792ec-e55b-259a-e9eb-50c138964653.png)


# 参考

- [An Object is not a Hash](http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/)
- [Is it bad practice to use Object.create(null) versus {}?](http://stackoverflow.com/questions/32262809/is-it-bad-practice-to-use-object-createnull-versus)


# 追記 (2016-11-04)

ハッシュ目的には [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) を使えばいいのでは？というコメントをいただきました。

Android 5.1, IE 11から部分的に使えるようですね。

http://kangax.github.io/compat-table/es6/#test-Map

ありがとうございます。


# 追記 - ObjectとMapの使い分け (2016-11-07)

MDNに [Objects and maps compared](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_and_maps_compared) というセクションがありました。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map#オブジェクトとマップの比較

キーが動的な場合や、任意の型が入る場合、もしくは反復処理をしたい場合には積極的に `Map` を推奨しているようです。