Was ist der Unterschied zwischen ES6 Map und WeakMap?

90

Auf dieser und dieser MDN-Seite scheint der einzige Unterschied zwischen Maps und WeakMaps darin zu liegen, dass WeakMaps keine "size" -Eigenschaft besitzt. Aber ist das wahr? Was ist der Unterschied zwischen ihnen?

Dmitrii Sorin
quelle
Der Effekt ist auf den GC. Bei WeakMaps können die Schlüssel gesammelt werden.
John Dvorak
@ JanDvorak Es gibt kein Beispiel für MDN. Wie aWeakMap.get (Schlüssel); // sag, 2 ... (GC-Aktion) ... aWeakMap.get (Schlüssel); // sagen, undefiniert
Dmitrii Sorin
1
Ihr Beispiel ist unmöglich. keykann nicht gesammelt werden, da Sie darauf verweisen.
John Dvorak
1
Die Entwurfsentscheidung ist, dass GC-Aktionen in Javascript unsichtbar sind. Sie können nicht beobachten, wie GC seine Sache macht.
John Dvorak
1
Weitere Informationen zu diesem Problem finden Sie in dieser Antwort .
Benjamin Gruenbaum

Antworten:

52

Auf derselben Seite, Abschnitt " Warum schwache Karte? " :

Der erfahrene JavaScript-Programmierer wird feststellen, dass diese API in JavaScript mit zwei Arrays (eines für Schlüssel, eines für Werte) implementiert werden kann, die von den 4 API-Methoden gemeinsam genutzt werden. Eine solche Implementierung hätte zwei Hauptprobleme. Die erste ist eine O (n) -Suche (n ist die Anzahl der Schlüssel in der Karte). Das zweite Problem ist ein Speicherverlust. Bei manuell geschriebenen Karten würde das Array von Schlüsseln Verweise auf Schlüsselobjekte behalten und verhindern, dass diese durch Müll gesammelt werden. In nativen WeakMaps werden Verweise auf Schlüsselobjekte "schwach" gehalten , was bedeutet, dass sie die Speicherbereinigung nicht verhindern, falls es keinen anderen Verweis auf das Objekt geben würde.

Aufgrund schwacher Referenzen sind WeakMap-Schlüssel nicht aufzählbar (dh es gibt keine Methode, mit der Sie eine Liste der Schlüssel erhalten). Wenn dies der Fall wäre, würde die Liste vom Zustand der Speicherbereinigung abhängen, was zu einem Nichtdeterminismus führen würde.

[Und deshalb haben sie auch kein sizeEigentum]

Wenn Sie eine Liste mit Schlüsseln haben möchten, sollten Sie diese selbst pflegen. Es gibt auch einen ECMAScript-Vorschlag, der darauf abzielt, einfache Mengen und Karten einzuführen, die keine schwachen Referenzen verwenden und aufzählbar wären.

- das wäre das "normale" Maps . Nicht bei MDN erwähnt, aber im Einklang Vorschlag , diejenigen , auch haben items, keysund valuesGenerator Methoden und die Umsetzung IteratorSchnittstelle .

Bergi
quelle
Hat new Map().get(x)also ungefähr die gleiche Suchzeit wie das Lesen einer Eigenschaft von einem einfachen Objekt?
Alexander Mills
1
@AlexanderMills Ich verstehe nicht, was dies mit der Frage zu tun hat, aber hier sind einige Daten . Im Allgemeinen sind sie ja ähnlich , und Sie sollten die entsprechende verwenden .
Bergi
Mein Verständnis ist also, dass Map ein internes Array verwaltet, um seinen Schlüssel aufgrund dieses Arrays beizubehalten. Garbage Collector kann die Referenz nicht unterlassen. In WeekMap gibt es kein Array, in dem Schlüssel verwaltet werden, sodass Schlüssel ohne Referenz im Müll gesammelt werden können.
Mohan Ram
@MohanRam A hat WeakMapimmer noch ein Array (oder eine andere Sammlung) von Einträgen. Es teilt dem Garbage Collector lediglich mit, dass dies schwache Referenzen sind .
Bergi
Warum wird dann die Iteration für WeekMap-Schlüssel nicht unterstützt?
Mohan Ram
92

Beide verhalten sich unterschiedlich, wenn ein Objekt, auf das durch ihre Schlüssel / Werte verwiesen wird, gelöscht wird. Nehmen wir den folgenden Beispielcode:

var map = new Map();
var weakmap = new WeakMap();

(function(){
    var a = {x: 12};
    var b = {y: 12};

    map.set(a, 1);
    weakmap.set(b, 2);
})()

Die obige IIFE ausgeführt gibt es keine Art , wie wir verweisen können {x: 12}und {y: 12}mehr. Der Garbage Collector löscht den Schlüssel-b-Zeiger aus „WeakMap“ und entfernt ihn ebenfalls {y: 12}aus dem Speicher. Im Fall von "Map" entfernt der Garbage Collector jedoch keinen Zeiger aus "Map" und auch nicht {x: 12}aus dem Speicher.

Zusammenfassung: Mit WeakMap kann der Garbage Collector seine Aufgabe ausführen, Map jedoch nicht.

Referenzen: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/

kshirish
quelle
11
Warum wird es nicht aus dem Speicher entfernt? Weil du immer noch darauf verweisen kannst! map.entries().next().value // [{x:12}, 1]
Bergi
4
Es ist keine selbst aufgerufene Funktion. Es ist ein sofort aufgerufener Funktionsausdruck. benalman.com/news/2010/11/…
Olson.dev
was ist dann der Unterschied zwischen weakmap und Objekt
Muhammad Umer
@MuhammadUmer: Objekt kann nur Zeichenfolgenschlüssel haben, während WeakMapes nur nicht primitive Schlüssel haben kann (keine Zeichenfolgen oder Zahlen oder Symbols als Schlüssel, nur Arrays, Objekte, andere Karten usw.).
Ahmed Fasih
1
@nnnnnn Ja, das ist der Unterschied, es ist immer noch in der - Map aber nicht in derWeakMap
Alexander Derck
75

Vielleicht wird die nächste Erklärung für jemanden klarer.

var k1 = {a: 1};
var k2 = {b: 2};

var map = new Map();
var wm = new WeakMap();

map.set(k1, 'k1');
wm.set(k2, 'k2');

k1 = null;
map.forEach(function (val, key) {
    console.log(key, val); // k1 {a: 1}
});

k2 = null;
wm.get(k2); // undefined

Wie Sie sehen, k1können wir nach dem Entfernen des Schlüssels aus dem Speicher weiterhin innerhalb der Karte darauf zugreifen. Gleichzeitig wird durch Entfernen des k2Schlüssels von WeakMap dieser auch wmals Referenz entfernt.

Aus diesem Grund hat WeakMap keine aufzählbaren Methoden wie forEach, da es keine Liste von WeakMap-Schlüsseln gibt, sondern nur Verweise auf andere Objekte.

Rax Wunter
quelle
10
In der letzten Zeile ist wm.get (null) natürlich undefiniert.
DaNeSh
8
Bessere Antwort als Kopieren und Einfügen von der Mozilla-Site, ein dickes Lob.
Joel Hernandez
2
in der forEach, (key, val)sein sollte eigentlich(val, key)
Miguel Mota
Unglaublich, wie ein Beispiel, das keinen Sinn macht, so viele positive
Stimmen
34

Ein weiterer Unterschied (Quelle: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Schlüssel von WeakMaps sind nur vom Typ Objekt. Primitive Datentypen als Schlüssel sind nicht zulässig (z. B. kann ein Symbol kein WeakMap-Schlüssel sein).

Eine Zeichenfolge, eine Zahl oder ein Boolescher Wert können auch nicht als WeakMapSchlüssel verwendet werden. A Map kann primitive Werte für Schlüssel verwenden.

w = new WeakMap;
w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key

m = new Map
m.set('a', 'b'); // Works
Trevor Dixon
quelle
6
Für den Fall, dass sich jemand wundert: Ich kann mir vorstellen, dass der Grund dafür ist: Sie können keine Verweise auf primitive Typen behalten oder weitergeben. Der Schlüssel in einer WeakMap wäre also die einzige Referenz, die es je gab. Auf diese Weise wäre eine Speicherbereinigung nicht möglich. Ich weiß nicht, ob schwache Referenzen unmöglich sind oder einfach keinen Sinn ergeben. In beiden Fällen muss der Schlüssel jedoch etwas sein, auf das nur schwach verwiesen werden kann.
Andreas Linnert
3

Aus Javascript.info

Karte - Wenn wir ein Objekt als Schlüssel in einer regulären Karte verwenden, ist dieses Objekt auch vorhanden, solange die Karte vorhanden ist. Es belegt Speicher und kann nicht Müll gesammelt werden.

let john = { name: "John" };
let array = [ john ];
john = null; // overwrite the reference

// john is stored inside the array, so it won't be garbage-collected
// we can get it as array[0]

Ähnlich wie wenn ein Objekt als Schlüssel in einer regulären Karte verwendet wird, existiert dieses Objekt auch, solange die Karte existiert. Es belegt Speicher und kann nicht Müll gesammelt werden

let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overwrite the reference

// john is stored inside the map,
// we can get it by using map.keys()

WeakMap - Wenn wir nun ein Objekt als Schlüssel verwenden und keine anderen Verweise auf dieses Objekt vorhanden sind, wird es automatisch aus dem Speicher (und aus der Karte) entfernt.

let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference

// john is removed from memory!
Avadhut Thorat
quelle
3

WeapMap in Javascript enthält keine Schlüssel oder Werte. Es bearbeitet lediglich den Schlüsselwert mithilfe einer eindeutigen ID und definiert eine Eigenschaft für das Schlüsselobjekt.

Da es die Eigenschaft to key objectby method definiert Object.definePropert(), darf der Schlüssel kein primitiver Typ sein .

und auch weil WeapMap keine Schlüsselwertpaare enthält, können wir die Längeneigenschaft von schwache Karte nicht erhalten.

und auch manipulierter Wert wird dem Schlüsselobjekt zurück zugewiesen, Garbage Collector kann Schlüssel leicht sammeln, wenn er nicht verwendet wird.

Beispielcode für die Implementierung.

if(typeof WeapMap != undefined){
return;
} 
(function(){
   var WeapMap = function(){
      this.__id = '__weakmap__';
   }
        
   weakmap.set = function(key,value){
       var pVal = key[this.__id];
        if(pVal && pVal[0] == key){
           pVal[1]=value;
       }else{
          Object.defineProperty(key, this.__id, {value:[key,value]});
          return this;
        }
   }

window.WeakMap = WeakMap;
})();

Referenz der Umsetzung

Ravi Sevta
quelle
1
Um klar zu sein, funktioniert diese Implementierung nur zur Hälfte. Es ist nicht möglich, dasselbe Objekt als Schlüssel in mehreren schwachen Karten zu verwenden. Es funktioniert auch nicht für eingefrorene Objekte. Und natürlich wird die Zuordnung an jeden weitergegeben, der einen Verweis auf das Objekt hat. Die erste kann mit Symbolen festgelegt werden, die beiden letzteren jedoch nicht.
Andreas Rossberg
@AndreasRossberg In dieser Implementierung habe ich fest codiert hinzugefügt id, aber dies sollte eindeutig sein, indem etwas Math.random und Date.now () usw. verwendet werden. Und durch Hinzufügen dieser dynamischen ID kann der erste Punkt gelöst werden. Könnten Sie mir bitte eine Lösung für die letzten beiden Punkte geben?
Ravi Sevta
Das erste Problem wird eleganter durch die Verwendung von Symbolen gelöst. Die beiden letzteren können in JS nicht gelöst werden, weshalb WeakMap ein Grundelement in der Sprache sein muss.
Andreas Rossberg
1

WeakMap Schlüssel müssen Objekte sein, keine primitiven Werte.

let weakMap = new WeakMap();

let obj = {};

weakMap.set(obj, "ok"); // works fine (object key)

// can't use a string as the key
weakMap.set("test", "Not ok"); // Error, because "test" is not an object

Warum????

Sehen wir uns das folgende Beispiel an.

let user = { name: "User" };

let map = new Map();
map.set(user, "...");

user = null; // overwrite the reference

// 'user' is stored inside the map,
// We can get it by using map.keys()

Wenn wir ein Objekt als Schlüssel in einem regulären Objekt verwenden, existiert dieses Objekt auch Map, solange Mapes existiert. Es belegt Speicher und kann nicht Müll gesammelt werden.

WeakMapist in diesem Aspekt grundlegend anders. Es verhindert nicht die Speicherbereinigung von Schlüsselobjekten.

let user = { name: "User" };

let weakMap = new WeakMap();
weakMap.set(user, "...");

user = null; // overwrite the reference

// 'user' is removed from memory!

Wenn wir ein Objekt als Schlüssel verwenden und es keine anderen Verweise auf dieses Objekt gibt, wird es automatisch aus dem Speicher (und aus der Karte) entfernt.

WeakMap unterstützt keine Iteration und Methoden () , Werte () , Einträge () , daher gibt es keine Möglichkeit, alle Schlüssel oder Werte daraus abzurufen.

WeakMap hat nur die folgenden Methoden:

  • schwachMap.get (Schlüssel)
  • schwachMap.set (Schlüssel, Wert)
  • schwachMap.delete (Schlüssel)
  • schwachMap.has (Schlüssel)

Das ist offensichtlich, als ob ein Objekt alle anderen Referenzen verloren hat (wie 'Benutzer' im obigen Code), dann soll es automatisch Müll gesammelt werden. Aber technisch ist es nicht genau angegeben, wann die Bereinigung stattfindet.

Die JavaScript-Engine entscheidet das. Es kann sich dafür entscheiden, die Speicherbereinigung sofort durchzuführen oder zu warten und die Bereinigung später durchzuführen, wenn weitere Löschvorgänge auftreten. Technisch gesehen ist die aktuelle Elementanzahl von a WeakMapalso nicht bekannt. Der Motor hat es möglicherweise gereinigt oder nicht oder teilweise. Aus diesem Grund werden Methoden, die auf alle Schlüssel / Werte zugreifen, nicht unterstützt.

Hinweis: - Der Hauptanwendungsbereich von WeakMap ist ein zusätzlicher Datenspeicher. Als würde man ein Objekt zwischenspeichern, bis dieses Objekt Müll gesammelt hat.

Pravin Divraniya
quelle