466

この機能を発見しました:

マップ: マップ オブジェクトは単純なキー/値マップです。

それは私を混乱させました。通常の JavaScript オブジェクトは辞書ですがMap、辞書との違いは何ですか? 概念的には、それらは同一です(Stack Overflowに関する別の質問によると)

ドキュメントも役に立ちません:

Map オブジェクトは、キーと値の両方が任意の ECMAScript 言語値である可能性がある、キーと値のペアのコレクションです。個別のキー値は、Map のコレクション内の 1 つのキーと値のペアでのみ発生する可能性があります。マップの作成時に選択された比較アルゴリズムを使用して識別された個別のキー値。

Map オブジェクトは、要素を挿入順に繰り返すことができます。Map オブジェクトは、ハッシュ テーブルまたはその他のメカニズムを使用して実装する必要があります。これらのメカニズムは、平均して、コレクション内の要素数に対してサブリニアなアクセス時間を提供します。この Map オブジェクトの仕様で使用されるデータ構造は、Map オブジェクトの必要な観察可能なセマンティクスを記述することのみを目的としています。実行可能な実装モデルを意図したものではありません。

…私にはまだオブジェクトのように聞こえるので、明らかに何かを見逃しています。

MapJavaScript が (十分にサポートされている)オブジェクトを取得するのはなぜですか? それは何をするためのものか?

4

13 に答える 13

416

MDNによると:

Map オブジェクトは、要素を挿入順に繰り返すことができます。for..ofループは、繰り返しごとに [key, value] の配列を返します。

オブジェクトは、キーを値に設定したり、それらの値を取得したり、キーを削除したり、キーに何かが格納されているかどうかを検出したりできるという点でマップに似ています。このため、オブジェクトは歴史的にマップとして使用されてきました。ただし、オブジェクトとマップには、マップをより使いやすくする重要な違いがあります。

オブジェクトにはプロトタイプがあるため、マップにはデフォルトのキーがあります。ただし、これは map = Object.create(null) を使用してバイパスできます。オブジェクトのキーは文字列であり、Map の任意の値にすることができます。オブジェクトのサイズを手動で追跡する必要がある場合でも、マップのサイズを簡単に取得できます。

地図

iterability-in-order は、すべてのブラウザーで同じパフォーマンスを保証するという理由から、開発者が長い間望んでいた機能です。だから私にとって、それは大きなものです。

メソッドはmyMap.has(key)特に便利で、myMap.sizeプロパティも便利です。

于 2013-08-30T21:53:14.233 に答える
167

主な違いは、オブジェクトは文字列とシンボル キーのみをサポートするのに対し、マップは多かれ少なかれすべてのキー タイプをサポートすることです。

私がそうすればobj[123] = true、私はよりもむしろObject.keys(obj)得るでしょう。Map はキーのタイプを保持し、それを返します。これは素晴らしいことです。マップでは、オブジェクトをキーとして使用することもできます。伝統的にこれを行うには、オブジェクトをハッシュするための何らかの一意の識別子をオブジェクトに与える必要がありました (標準の一部として JavaScript のようなものを見たことはないと思います)。マップは順序の保存も保証するので、全体的に保存に適しています。["123"][123][123]getObjectId

実際のマップとオブジェクトの間には、いくつかの長所と短所があります。オブジェクトは、JavaScript のコアに非常に緊密に統合されているため、長所と短所の両方を獲得しています。

当面の利点は、要素へのアクセスを容易にするオブジェクトの構文サポートがあることです。また、JSON を使用して直接サポートすることもできます。ハッシュとして使用する場合、プロパティをまったく持たないオブジェクトを取得するのは面倒です。デフォルトでは、オブジェクトをハッシュ テーブルとして使用する場合、それらは汚染され、hasOwnPropertyプロパティにアクセスするときにオブジェクトを呼び出さなければならないことがよくあります。ここでは、デフォルトでオブジェクトがどのように汚染されているか、およびハッシュとして使用するために汚染されていないオブジェクトを作成する方法を確認できます。

({}).toString
    toString() { [native code] }
JSON.parse('{}').toString
    toString() { [native code] }
(Object.create(null)).toString
    undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
    undefined

オブジェクトの汚染は、コードを煩わしくしたり、遅くしたりするだけでなく、セキュリティに影響を与える可能性もあります。

オブジェクトは純粋なハッシュ テーブルではありませんが、それ以上のことを行おうとしています。hasOwnProperty、長さがなかなか取れない( )などの頭痛がありObject.keys(obj).lengthます。オブジェクトは純粋にハッシュ マップとして使用されることを意図したものではなく、動的に拡張可能なオブジェクトとしても使用されるため、それらを純粋なハッシュ テーブルとして使用すると問題が発生します。

各種共通操作の比較・一覧:

Object:
   var o = {};
   var o = Object.create(null);
   o.key = 1;
   o.key += 10;
   for(let k in o) o[k]++;
   var sum = 0;
   for(let v of Object.values(m)) sum += v;
   if('key' in o);
   if(o.hasOwnProperty('key'));
   delete(o.key);
   Object.keys(o).length
Map:
   var m = new Map();
   m.set('key', 1);
   m.set('key', m.get('key') + 10);
   m.foreach((k, v) => m.set(k, m.get(k) + 1));
   for(let k of m.keys()) m.set(k, m.get(k) + 1);
   var sum = 0;
   for(let v of m.values()) sum += v;
   if(m.has('key'));
   m.delete('key');
   m.size();

他にもいくつかのオプション、アプローチ、方法論などがあります。さまざまな浮き沈み (パフォーマンス、簡潔、ポータブル、拡張可能など) があります。オブジェクトは言語の中核であるというのは少し奇妙なので、それらを操作するための静的メソッドがたくさんあります。

マップがキーの型を保持し、オブジェクトのようなものをキーとしてサポートできるという利点に加えて、オブジェクトが持つ副作用から隔離されています。Map は純粋なハッシュです。同時にオブジェクトになろうとしても混乱はありません。プロキシ機能を使用してマップを簡単に拡張することもできます。オブジェクトには現在 Proxy クラスがありますが、パフォーマンスとメモリの使用量は厳しく、実際、Map for Objects のように見える独自のプロキシを作成すると、現在 Proxy よりも優れたパフォーマンスを発揮します。

Maps の大きな欠点は、JSON で直接サポートされていないことです。解析は可能ですが、いくつかのハングアップがあります:

JSON.parse(str, (k,v) => {
    if(typeof v !== 'object') return v;
    let m = new Map();
    for(k in v) m.set(k, v[k]);
    return m;
});

上記は深刻なパフォーマンス ヒットをもたらし、文字列キーもサポートしません。JSON エンコーディングはさらに難しく、問題があります (これは多くのアプローチの 1 つです)。

// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
    return JSON.stringify({
        keys: Array.from(this.keys()),
        values: Array.from(this.values())
    });
};

純粋にマップを使用している場合、これはそれほど悪くはありませんが、タイプを混合したり、非スカラー値をキーとして使用したりする場合に問題が発生します (JSON がそのままでその種の問題に完全であるとは限りません。IE 循環オブジェクト参照)。テストはしていませんが、stringify に比べてパフォーマンスが大幅に低下する可能性があります。

他のスクリプト言語では、Map、Object、および Array に明示的な非スカラー型があるため、多くの場合、このような問題はありません。Web 開発は、PHP がプロパティに A/M を使用して配列/マップをオブジェクトとマージし、JavaScript が M/O を拡張する配列とマップ/オブジェクトをマージするなどの処理を行わなければならない非スカラー型の場合は苦痛です。複雑な型をマージすることは、高レベルのスクリプト言語にとって天災です。

これまでのところ、これらは主に実装に関する問題ですが、基本的な操作のパフォーマンスも重要です。性能もエンジンや使い方次第なので複雑です。間違いを排除することはできないので、テストを一粒の塩で受けてください(急がなければなりません)。また、独自のテストを実行して確認する必要があります. 非常に大きなオブジェクト/マップに対する Chrome でのテストによると、O(1) ではなくキーの数に明らかに何らかの形で比例する削除のために、オブジェクトのパフォーマンスが低下します。

Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2

取得と更新に関しては明らかに Chrome の方が優れていますが、削除のパフォーマンスはひどいものです。この場合、マップはわずかに多くのメモリを使用しますが (オーバーヘッド)、1 つのオブジェクト/マップのみが何百万ものキーでテストされているため、マップのオーバーヘッドの影響は十分に表現されていません。メモリ管理を使用すると、プロファイルを正しく読み取っている場合、オブジェクトもより早く解放されるように見えます。これは、オブジェクトに有利な 1 つの利点である可能性があります。

この特定のベンチマークの Firefox では、別の話です。

Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1

この特定のベンチマークでは、Firefox のオブジェクトから削除しても問題は発生しませんが、他のベンチマークでは、特に Chrome のように多くのキーがある場合に問題が発生することをすぐに指摘しておきます。大規模なコレクションでは、マップは Firefox の方が明らかに優れています。

しかし、これで話は終わりではありません。小さなオブジェクトやマップがたくさんある場合はどうでしょうか。私はこれの簡単なベンチマークを行いましたが、上記の操作で少数のキーで最高のパフォーマンスを発揮する包括的なもの (設定/取得) ではありません。このテストは、メモリと初期化に関するものです。

Map Create: 69    // new Map
Object Create: 34 // {}

これらの数値もさまざまですが、基本的には Object が優勢です。場合によっては、マップに対するオブジェクトの差が極端に大きくなります (最大 10 倍) が、平均して約 2 ~ 3 倍優れています。極端なパフォーマンスのスパイクは、両方の方法で機能するようです。これを Chrome でのみテストし、作成してメモリ使用量とオーバーヘッドをプロファイリングしました。Chrome では、キーが 1 つあるマップは、キーが 1 つあるオブジェクトよりも約 30 倍多くのメモリを使用しているように見えることに非常に驚きました。

上記のすべての操作 (4 つのキー) で多くの小さなオブジェクトをテストするには:

Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139

メモリ割り当てに関しては、これらは解放/ GCに関して同じように動作しましたが、 Map は 5 倍のメモリを使用しました。このテストでは 4 つのキーを使用しましたが、前回のテストでは 1 つのキーのみを設定したため、メモリ オーバーヘッドの削減が説明されます。このテストを数回実行しましたが、マップ/オブジェクトは、全体的な速度の点で、Chrome 全体で多かれ少なかれ首と首を争っています。小さなオブジェクト用の Firefox では、マップ全体よりも明らかにパフォーマンスが優れています。

もちろん、これには大きく異なる可能性のある個々のオプションは含まれていません。これらの数値を使用してマイクロ最適化することはお勧めしません。ここから得られることは、経験則として、非常に大きなキー値ストアにはマップを、小さなキー値ストアにはオブジェクトをより強く考慮するということです。

それを超えて、これら2つの最良の戦略は、それを実装し、最初に機能させることです. プロファイリングを行うときは、オブジェクト キーの削除のケースで見られるように、エンジンの癖が原因で、見たときには遅いとは思わないことが時々信じられないほど遅くなる可能性があることに留意することが重要です。

于 2016-06-23T14:09:20.647 に答える
35

概要:

  • Object: データがキーと値のペアとして格納されるデータ構造。オブジェクトでは、キーは数値、文字列、または記号でなければなりません。値は何でもよいので、他のオブジェクト、関数なども同様です。オブジェクトは順序付けされていないデータ構造です。つまり、キーと値のペアの挿入順序は記憶されていません。
  • ES6 Map: データがキーと値のペアとして格納されるデータ構造。一意のキーが値にマップされます。キーと値はどちらも任意のデータ型にすることができます。マップは反復可能なデータ構造です。これは、挿入の順序が記憶され、for..ofループなどで要素にアクセスできることを意味します。

主な違い:

  • AMapは順序付けられていて反復可能ですが、オブジェクトは順序付けられておらず、反復可能ではありません

  • Mapオブジェクトは数値、文字列、または記号のみをキーとして持つことができますが、任意のタイプのデータをキーとして配置できます。

  • AMapは から継承しMap.prototypeます。これにより、Mapオブジェクトの操作が非常に簡単になるあらゆる種類のユーティリティ関数とプロパティが提供されます。

例:

物体:

let obj = {};

// adding properties to a object
obj.prop1 = 1;
obj[2]    =  2;

// getting nr of properties of the object
console.log(Object.keys(obj).length)

// deleting a property
delete obj[2]

console.log(obj)

地図:

const myMap = new Map();

const keyString = 'a string',
    keyObj = {},
    keyFunc = function() {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

console.log(myMap.size); // 3

// getting the values
console.log(myMap.get(keyString));    // "value associated with 'a string'"
console.log(myMap.get(keyObj));       // "value associated with keyObj"
console.log(myMap.get(keyFunc));      // "value associated with keyFunc"

console.log(myMap.get('a string'));   // "value associated with 'a string'"
                         // because keyString === 'a string'
console.log(myMap.get({}));           // undefined, because keyObj !== {}
console.log(myMap.get(function() {})) // undefined, because keyFunc !== function () {}

出典: MDN

于 2018-10-28T18:36:15.527 に答える
8

モジラによると

JavaScriptのオブジェクトとマップの例を簡単に説明します。

オブジェクト- マップと同じ概念に従います。つまり、データを格納するためにキーと値のペアを使用します。ただし、特定の状況でマップのパフォーマンスを向上させるわずかな違いがあります。

Map-は、データをペアの形で保存するのに役立つデータ構造です。ペアは、一意のキーと、キーにマップされた値で構成されます。重複防止に役立ちます。

主な違い

  • Map はオブジェクトのインスタンスですが、その逆は当てはまりません。

var map = new Map();
var obj = new Object();
console.log(obj instanceof Map);   // false
console.log(map instanceof Object);  // true

  • Object では、key-field のデータ型は整数、文字列、および記号に制限されています。Map では、key-field は任意のデータ型 (整数、配列、オブジェクト) にすることができます。

var map = new Map();//Empty
map.set(1,'1');
map.set('one', 1);
map.set('{}', {name:'Hello, World!'});
map.set(12.3, 12.3)
map.set([12],[12345])

for(let [key,value] of map.entries())
  console.log(key+'---'+value)

  • Map では、要素の元の順序が保持されます。これはオブジェクトの場合には当てはまりません。

let obj ={
  1:'1',
  'one':1,
  '{}': {name:'Hello world'},
  12.3:12.3,
  [12]:[100]
}
console.log(obj)

于 2020-01-01T08:30:06.140 に答える
5

これは私がそれを覚えるための短い方法です:KOI

  1. キー。オブジェクト キーは文字列または記号です。マップ キーは、数字 (1 と "1" は異なります)、オブジェクトNaNなどにすることもできます===。1 つの例外を除いて、キーを区別するために使用しNaN !== NaNますが、キーとして使用できNaNます。
  2. 注文。挿入順序が記憶されます。そう[...map]または[...map.keys()]特定の順序があります。
  3. インターフェース。オブジェクト:obj[key]またはobj.a(一部の言語で[]は、[]=実際にはインターフェイスの一部です)。Map にはget()set()、などがあります。 を使用できることに注意してください。ただし、has()それはプレーンな JavaScript オブジェクトとして使用しています。delete()map[123]
于 2019-12-28T14:28:43.727 に答える