概要
JavaScriptの Object.create(null)
はなぜ使われるのか。
{}
での初期化とは何が違うのか。
きっかけ
Vuex のソースコードを読んでいて、 Object.create(null)
という見慣れない記述を見つけました。
リテラル {}
ではいけない理由が気になったので、なぜなのか調べてみました。
対応ブラウザ
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.create
は prototype
を指定してオブジェクトを生成する ECMAScript 5 からの仕様のようです。
var a = {}
var b = Object.create(Object.prototype)
この a
と b
は実質同じ挙動になります。
パフォーマンス?
よくある理由として考えたのはパフォーマンスの理由が挙げられます。
ところが調べてみると、リテラルでの生成の方が数倍高速に動作するようです。
※ 2016-11-07追記: リンクを差し替え
ハッシュとしてのオブジェクトの利用
JavaScriptでオブジェクト使うシーンとして多いのが、ハッシュとしてキャッシュやデータ保存目的で使われるシーンですよね。
気軽に「key-value形式」で保存できるので様々な箇所で使われますが、ユーザーの入力値をキーとして使う場合に問題が出てきます。
例えば、users
というオブジェクトをハッシュのように利用して、ユーザーの入力値(この例では account
とname
の値のセット)を保存していく場合を考えてみます。
var users = {} // { アカウント名: 名前 }
function addUser(account, name) {
// キーが既に登録されていなければ保存
if (!users[account]) {
users[account] = name
}
}
addUser('taro', '山田太郎')
addUser('taro', '田中太郎') // 保存されない
taro
というキーははじめて設定するので、値山田太郎
は無事に保存できます。
二度目に taro
というキーで保存使用すると、既に存在するので無視されます。
では、アカウント名として constructor
という厳つい単語を指定をした場合にどうなるでしょうか?
addUser('constructor', '鈴木次郎') // 保存されない
この場合、 users.prototype
がconstructor
というメソッドを持っているので、登録できないことになってしまいます。
他にも、 toString
や valueOf
なども利用できません。
これは、 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-04)
ハッシュ目的には Map を使えばいいのでは?というコメントをいただきました。
Android 5.1, IE 11から部分的に使えるようですね。
ありがとうございます。
追記 - ObjectとMapの使い分け (2016-11-07)
MDNに Objects and maps compared というセクションがありました。
キーが動的な場合や、任意の型が入る場合、もしくは反復処理をしたい場合には積極的に Map
を推奨しているようです。