Knowledge Base

お知らせや身辺のことを綴っています。

JavaScript の { } に注意!

プロトタイプメソッドの存在に注意

JavaScript でオブジェクトを取り扱うときは、プロトタイプメソッドの存在に注意する。

特に、空のオブジェクトを作成するつもりで{} という文字列リテラルで作成したオブジェクトは、実際にはプロトタイプメソッドを継承している。

console.log({}['__proto__'])
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

これらのプロパティへ値を代入する操作は、エラーこそ吐かないが実際にはその値は代入されない。

obj = {}
obj['__proto__'] = "Hello"
console.log(obj['__proto__']) 
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
// ↑ 値が変わっていない!

対処法

このような問題を避けるには、Null オブジェクトをプロトタイプとしたカスタムオブジェクトを Object.create() メソッドで作成する方法が有効である。かくして作成されたオブジェクトは、プロトタイプを持たないので、参照しても undefined になる。

console.log(Object.create(null)['__proto__'])
// undefined

カスタムオブジェクトと Null オブジェクト
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create#カスタムオブジェクトとnullオブジェクト

Map を利用しよう

他方、この方法でオブジェクトを生成すると、本来オブジェクトに適用できるはずの一部のプロトタイプメソッドが使えなくなってしまうデメリットも存在するし、基本的にパフォーマンスが低下するそうなので、基本的にユニークなキー名とそれに対する値のペアを作りたいときには Map というデータ構造をつかうのがよさそう。

なお、MDNは Map とオブジェクトがどう違うかを懇切丁寧に比較してくれているので、ぜひ読んでみよう。

Object と Map の比較
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map#object_とmapの比較

まとめ

JavaScriptのオブジェクトをPythonの辞書やPHPの連想配列と同じような感覚で使うと、思わぬ陥穽にハマってしまうので注意すること。