オブジェクトの問題
問題はプロトタイプチェーンです。 新しいオブジェクトは、Object.prototypeからプロパティとメソッドを継承します。これにより、キーの存在を明確に判断できなくなる場合があります。
toString
メソッドを例にとると、
in
演算子を使用して同じ名前のキーをチェックすると、誤検知が発生します。
var map = {}; 'toString' in map; // true
これは、オブジェクトインスタンスでプロパティを見つけられない
in
演算子が、継承された値を検索する際にプロトタイプチェーンをさらに下に向かって見ているために発生します。 私たちの場合、これは
toString
メソッドです。 この問題を解決するために、
hasOwnProperty
メソッド
hasOwnProperty
。これは、現在のオブジェクトにのみプロパティが存在するかどうかを確認するように特別に設計されています。
var map = {}; map.hasOwnProperty('toString'); // false
この手法は、「hasOwnProperty」というキーに遭遇するまで正常に機能します。 このメソッドを上書きすると、その後の呼び出しにより、新しい値に応じて予測不能な結果またはエラーが生成されます。
var map = {}; map.hasOwnProperty = 'foo'; map.hasOwnProperty('hasOwnProperty'); // TypeError
この問題をすばやく修正します。 これを行うには、別の
hasOwnProperty
オブジェクトを使用し、オブジェクトのコンテキストで
hasOwnProperty
メソッドを呼び出します。
var map = {}; map.hasOwnProperty = 'foo'; {}.hasOwnProperty.call(map, 'hasOwnproperty'); // true
さて、この方法は既に問題なく機能していますが、それでも使用方法に制限があります。 たとえば、
for ... in
を使用
for ... in
てオブジェクトのプロパティを一覧表示するたびに、継承されたすべてのジャンクを除外する必要があります。
var map = {}; var has = {}.hasOwnProperty; for(var key in map){ if(has.call(map, key)){ // do something } }
しばらくすると、この方法はひどく疲れます。 より良い選択肢があることを神に感謝します。
裸のオブジェクト
きれいな連想配列を作成するための秘Theは、プロトタイプとそれに付随するすべての荷物を取り除くことです。 これを実現するには、ES5で導入された
Object.create
メソッドを使用します。 このメソッドの一意性は、新しいオブジェクトのプロトタイプを明示的に定義できることです。 たとえば、普通のオブジェクトをもう少し明確に作成します。
var obj = {}; // : var obj = Object.create(Object.prototype);
プロトタイプを選択できるという事実に加えて、このメソッドはプロトタイプをまったく選択しないオプションも提供します。代わりに
null
を渡すだけです。
var map = Object.create(null); map instanceof Object; // false Object.prototype.isPrototypeOf(map); // false Object.getPrototypeOf(map); // null
[[Prototype]]
がないため、名前の競合につまずくリスクがなくなるため、これらのベアオブジェクト(または辞書)は連想配列の作成に最適です。 そしてさらに良い! オブジェクトから継承されたすべてのメソッドとプロパティを奪った後、意図された目的(ストレージ)で使用しようとするとエラーが発生します。
var map = Object.create(null); map + ""; // TypeError: Cannot convert object to primitive value
プリミティブ値や文字列表現はありません。 ベアオブジェクトは、キーと値のペアのリポジトリとしてのみ動作することを目的としています。
hasOwnProperty
演算子はチェックなしで正常に動作するため、
hasOwnProperty
メソッドも存在しないことに
hasOwnProperty
してください。
var map = Object.create(null); 'toString' in map; // false
さらに、ループ
for ... in
退屈な
for ... in
ループは、より簡単になりました。 最後に、見た目通りに安全に記述できます。
var map = Object.create(null); for(var key in map){ // do something }
行った変更にもかかわらず、ドット表記や角かっこを使用したり、文字列に変換したり、
Object.prototype
メソッドのコンテキストとしてオブジェクトを使用するなど、オブジェクトに対して必要なことは何でもできます。
var map = Object.create(null); Object.defineProperties(map, { 'foo': { value: 1, enumerable: true }, 'bar': { value: 2, enumerable: false } }); map.foo; // 1 map['bar']; // 2 JSON.stringify(map); // {"foo":1} {}.hasOwnProperty.call(map, 'foo'); // true {}.propertyIsEnumerable.call(map, 'bar'); // false
型チェックのさまざまな方法でも機能します。
var map = Object.create(null); typeof map; // object {}.toString.call(map); // [object Object] {}.valueOf.call(map); // Object {}
これにより、新規および以前に作成されたアプリケーションの両方で、連想配列の作成に使用される通常のオブジェクトを、むき出しのオブジェクトに簡単に置き換えることができます。
おわりに
キーと値のペアの単純なリポジトリについて話すと、むき出しのオブジェクトはこのタスクに通常のオブジェクトよりもはるかにうまく対処し、開発者を不必要なものから救います。 より機能的なデータ構造の場合、ES6(ES2015)を待つ必要があります。これにより、 Map 、 Set、およびその他のオブジェクトの形式でネイティブの連想配列が提供されます。 それまでの間、このバラ色の瞬間は、裸の物体ではなく、最良の選択です。