621 Stimmen

Karte vs Objekt in JavaScript

Ich habe gerade diese Funktion entdeckt:

Karte: Kartenobjekte sind einfache Schlüssel-/Wert-Karten.

Das hat mich verwirrt. Reguläre JavaScript-Objekte sind Wörterbücher, also wie unterscheidet sich eine Karte von einem Wörterbuch? Konzeptionell sind sie identisch (laut einer anderen Frage auf Stack Overflow)

Auch die Dokumentation hilft nicht weiter:

Kartenobjekte sind Sammlungen von Schlüssel/Wert-Paaren, bei denen sowohl die Schlüssel als auch die Werte beliebige ECMAScript-Sprachwerte sein können. Ein bestimmter Schlüsselwert darf nur in einem Schlüssel/Wert-Paar innerhalb der Sammlung der Karte vorkommen. Unterschiedliche Schlüsselwerte werden mit einem Vergleichsalgorithmus diskriminiert, der beim Erstellen der Karte ausgewählt wird.

Ein Kartenobjekt kann seine Elemente in Einfügereihenfolge durchlaufen. Das Kartenobjekt muss mit Hilfe von Hash-Tabellen oder anderen Mechanismen implementiert sein, die im Durchschnitt Zugriffszeiten bieten, die sublinear zur Anzahl der Elemente in der Sammlung sind. Die in dieser Spezifikation von Kartenobjekten verwendeten Datenstrukturen sollen nur die erforderlichen beobachtbaren Semantiken von Kartenobjekten beschreiben. Sie sind nicht als praktisches Implementierungsmodell gedacht.

...klingt für mich immer noch wie ein Objekt, also habe ich offensichtlich etwas übersehen.

Warum erhält JavaScript also ein (gut unterstütztes) Karte-Objekt? Was kann es?

516voto

Laut MDN:

Ein Map-Objekt kann seine Elemente in der Reihenfolge ihrer Einfügung durchlaufen - eine for..of-Schleife gibt bei jeder Iteration ein Array [Schlüssel, Wert] zurück.

und

Objekte ähneln Maps darin, dass beide es ermöglichen, Schlüssel auf Werte zu setzen, diese Werte abzurufen, Schlüssel zu löschen und zu erkennen, ob etwas unter einem Schlüssel gespeichert ist. Deshalb wurden Objekte historisch gesehen als Maps verwendet; es gibt jedoch wichtige Unterschiede zwischen Objekten und Maps, die die Verwendung einer Map besser machen.

Ein Objekt hat einen Prototyp, sodass es standardmäßige Schlüssel in der Map gibt. Dies kann jedoch umgangen werden, indem map = Object.create(null) verwendet wird. Die Schlüssel eines Objekts sind Zeichenfolgen, während sie für eine Map jeden beliebigen Wert haben können. Die Größe einer Map kann einfach abgerufen werden, während Sie die Größe eines Objekts manuell verfolgen müssen.

_Map_

Die Iterierbarkeit in Reihenfolge ist eine Funktion, die von Entwicklern schon lange gewünscht wird, teilweise, weil sie die gleiche Leistung in allen Browsern gewährleistet. Für mich ist das also eine wichtige Sache.

Die Methode myMap.has(key) wird besonders nützlich sein, ebenso wie die Eigenschaft myMap.size.

88voto

Mani Gandham Punkte 6471

Ein Object verhält sich wie ein Wörterbuch, weil JavaScript dynamisch typisiert ist, sodass Sie jederzeit Eigenschaften hinzufügen oder entfernen können.

Aber Map() ist viel besser, weil es:

  • Bereitstellt get, set, has und delete Methoden.
  • Akzeptiert jeden Typ für die Schlüssel anstelle von nur Strings.
  • Bietet einen Iterator für eine einfache Verwendung von for-of und behält die Reihenfolge der Ergebnisse bei.
  • Hat keine Randfälle mit Prototypen und anderen Eigenschaften, die während der Iteration oder des Kopierens auftreten.
  • Unterstützt Millionen von Elementen.
  • Ist sehr schnell.

Wenn Sie ein Wörterbuch benötigen, verwenden Sie also ein Map().

Wenn Sie jedoch nur Schlüssel verwenden, die auf Strings basieren, und maximale Leseleistung benötigen, könnten Objekte die bessere Wahl sein. Das liegt daran, dass JavaScript-Engines Objekte im Hintergrund zu C++-Klassen kompilieren und der Zugriffspfad für Eigenschaften viel schneller ist als ein Funktionsaufruf für Map().get().

Diese Klassen werden auch zwischengespeichert, sodass das Erstellen eines neuen Objekts mit genau denselben Eigenschaften dazu führt, dass die Engine eine vorhandene Hintergrundklasse wiederverwendet. Das Hinzufügen oder Entfernen einer Eigenschaft führt dazu, dass sich die Form der Klasse ändert und die Hintergrundklasse neu kompiliert wird, weshalb die Verwendung eines Objekts als Wörterbuch mit vielen Hinzufügungen und Löschungen sehr langsam ist, das Lesen bestehender Schlüssel ohne Änderung des Objekts jedoch sehr schnell ist.

Also, wenn Sie eine Arbeitslast mit viel Lesen und wenigen Schreibvorgängen und String-Schlüsseln haben, können Sie ein Object als leistungsstarkes Wörterbuch verwenden, aber für alles andere verwenden Sie ein Map().

50voto

Willem van der Veen Punkte 26043

Zusammenfassung:

  • Objekt: Eine Datenstruktur, in der Daten als Schlüssel-Wert-Paare gespeichert sind. In einem Objekt muss der Schlüssel eine Zahl, Zeichenfolge oder ein Symbol sein. Der Wert kann beliebig sein, also auch andere Objekte, Funktionen usw. Ein Objekt ist eine ungeordnete Datenstruktur, d. h. die Reihenfolge der Einfügung von Schlüssel-Wert-Paaren wird nicht gemerkt
  • ES6 Map: Eine Datenstruktur, in der Daten als Schlüssel-Wert-Paare gespeichert sind. In der ein eindeutiger Schlüssel auf einen Wert abgebildet wird. Sowohl der Schlüssel als auch der Wert können in jedem Datentyp sein. Eine Map ist eine durchlaufbare Datenstruktur. Das bedeutet, dass die Reihenfolge der Einfügung gemerkt wird und dass wir auf die Elemente z. B. in einer for..of-Schleife zugreifen können.

Wesentliche Unterschiede:

  • Ein Map ist ordentlich und durchlaufbar, während ein Objekt nicht geordnet und nicht durchlaufbar ist (im Sinne, dass sie keine [Symbol.iterator]-Eigenschaft haben. Sie können jedoch über die Schlüssel mithilfe der for..in-Syntax iterieren.)

  • Wir können jeden Datentyp als Schlüssel für eine Map verwenden, während Objekte nur eine Zahl, eine Zeichenfolge oder ein Symbol als Schlüssel haben können.

  • Eine Map erbt von Map.prototype. Dies bietet verschiedene Dienstprogrammfunktionen und Eigenschaften, die die Arbeit mit Map-Objekten deutlich vereinfachen.

Beispiel:

Objekt:

let obj = {};

// Eigenschaften zu einem Objekt hinzufügen
obj.prop1 = 1;
obj[2]    =  2;

// Anzahl der Eigenschaften des Objekts abrufen
console.log(Object.keys(obj).length)

// Eine Eigenschaft löschen
delete obj[2]

console.log(obj)

Map:

const myMap = new Map();

const keyString = 'eine Zeichenfolge',
    keyObj = {},
    keyFunc = function() {};

// Werte setzen
myMap.set(keyString, "Wert, der mit 'einer Zeichenfolge' verbunden ist");
myMap.set(keyObj, 'Wert, der mit keyObj verbunden ist');
myMap.set(keyFunc, 'Wert, der mit keyFunc verbunden ist');

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

// Werte abrufen
console.log(myMap.get(keyString));    // "Wert, der mit 'einer Zeichenfolge' verbunden ist"
console.log(myMap.get(keyObj));       // "Wert, der mit keyObj verbunden ist"
console.log(myMap.get(keyFunc));      // "Wert, der mit keyFunc verbunden ist"

console.log(myMap.get('eine Zeichenfolge'));   // "Wert, der mit 'einer Zeichenfolge' verbunden ist"
                         // weil keyString === 'eine Zeichenfolge'
console.log(myMap.get({}));           // undefined, weil keyObj !== {}
console.log(myMap.get(function() {})) // undefined, weil keyFunc !== function () {}

Quelle: MDN

47voto

Ich glaube, die folgenden Punkte wurden in den Antworten bisher nicht erwähnt und ich dachte, es lohnt sich, sie zu erwähnen.


Karten können größer sein

In Chrome kann ich 16,7 Millionen Schlüssel/Wert-Paare mit Map erhalten, im Vergleich zu 11,1 Millionen mit einem regulären Objekt. Fast genau 50% mehr Paare mit einer Map. Beide nehmen etwa 2 GB Speicher ein, bevor sie abstürzen, und daher denke ich, dass es mit der Speicherbegrenzung durch Chrome zusammenhängen könnte (Ja, versuchen Sie, 2 Maps zu füllen, und Sie gelangen nur bis zu 8,3 Millionen Paaren, bevor es abstürzt). Sie können es selbst mit diesem Code testen (führen Sie sie getrennt und nicht gleichzeitig aus, offensichtlich):

var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"tausend") }
}
// verglichen mit:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"tausend") }
}

Objekte haben bereits einige Eigenschaften/Schlüssel

Dies hat mich schon einmal durcheinander gebracht. Reguläre Objekte haben toString, constructor, valueOf, hasOwnProperty, isPrototypeOf und eine Vielzahl von anderen bereits vorhandenen Eigenschaften. Dies mag für die meisten Anwendungsfälle kein großes Problem sein, hat aber in der Vergangenheit Probleme für mich verursacht.

Karten können langsamer sein:

Aufgrund des Overheads des .get-Funktionsaufrufs und des Mangels an interner Optimierung kann eine Map beträchtlich langsamer sein als ein einfaches altes JavaScript-Objekt für einige Aufgaben.

23voto

Dan Dascalescu Punkte 125523

Zusätzlich zu den anderen Antworten habe ich festgestellt, dass Maps unhandlicher und ausführlicher zu bedienen sind als Objekte.

obj[key] += x
// vs.
map.set(map.get(key) + x)

Dies ist wichtig, weil kürzerer Code schneller zu lesen ist, direkter aussagekräftig ist und besser im Kopf des Programmierers behalten wird.

Ein weiterer Aspekt: Da set() die Map zurückgibt, nicht den Wert, ist es unmöglich, Zuweisungen zu verschachteln.

foo = obj[key] = x;  // Macht, was du erwartest
foo = map.set(key, x)  // foo !== x; foo === map

Die Fehlersuche bei Maps ist ebenfalls schmerzhafter. Unten können Sie tatsächlich nicht sehen, welche Schlüssel sich in der Map befinden. Dazu müssten Sie Code schreiben.

Viel Glück beim Auswerten eines Map-Iterators

Objekte können von jeder IDE ausgewertet werden:

WebStorm wertet ein Objekt aus

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X