JavaScript
Vue.js
Vuex

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

More than 1 year has 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 を推奨しているようです。