Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

概要

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

きっかけ

Vuex のソースコードを読んでいて、 Object.create(null) という見慣れない記述を見つけました。
リテラル {} ではいけない理由が気になったので、なぜなのか調べてみました。

スクリーンショット 2016-11-03 午後2.25.52.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() によると、 Object.createprototype を指定してオブジェクトを生成する ECMAScript 5 からの仕様のようです。

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

この ab は実質同じ挙動になります。

パフォーマンス?

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

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

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

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

スクリーンショット 2016-11-07 午後1.35.19.png

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

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

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

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

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

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

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

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

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

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

他にも、 toStringvalueOf なども利用できません。

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

本当に空のオブジェクト

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

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

参考

追記 (2016-11-04)

ハッシュ目的には 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/ja/docs/Web/JavaScript/Reference/Global_Objects/Map#オブジェクトとマップの比較

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

tady
formrun(フォームラン)の創業者でした。会社売却後、現在は大企業で研鑽中 mailto:a.dat.jp@gmail.com
https://twitter.com/tady_jp
recruitlifestyle
飲食・美容・旅行領域の情報サイトや『Airレジ』などの業務支援サービスなど、日常消費領域に関わるサービスの提供するリクルートグループの中核企業
http://www.recruit-lifestyle.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away