JavaScriptの実際の連想配列

JavaScriptでキーと値のペアを格納する簡単な手段としてオブジェクトリテラルを使用することは、長い間一般的でした。 それでも、オブジェクトリテラルはまだ実際の連想配列ではないため、状況によっては、その使用によって予期しない結果が生じる可能性があります。 JSは連想配列のネイティブ実装を提供していません(少なくともすべてのブラウザーではそうではありません)が、適切な機能を持ち、落とし穴のないオブジェクトに代わる優れた選択肢があります。



オブジェクトの問題



問題はプロトタイプチェーンです。 新しいオブジェクトは、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)を待つ必要があります。これにより、 MapSet、およびその他のオブジェクトの形式でネイティブの連想配列が提供されます。 それまでの間、このバラ色の瞬間は、裸の物体ではなく、最良の選択です。



All Articles