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、およびその他のオブジェクトの形式でネイティブの連想配列が提供されます。 それまでの間、このバラ色の瞬間は、裸の物体ではなく、最良の選択です。

Source: https://habr.com/ru/post/J259529/


All Articles