Was sind die tatsächlichen Verwendungszwecke von ES6 WeakMap?

397

Was sind die tatsächlichen Verwendungszwecke der WeakMapin ECMAScript 6 eingeführten Datenstruktur?

Da ein Schlüssel einer schwachen Karte einen starken Verweis auf den entsprechenden Wert erstellt und sicherstellt, dass ein Wert, der in eine schwache Karte eingefügt wurde, niemals verschwindet, solange sein Schlüssel noch aktiv ist, kann er nicht für Memotabellen verwendet werden. Caches oder alles andere, für das Sie normalerweise schwache Referenzen, Karten mit schwachen Werten usw. verwenden würden.

Es scheint mir, dass dies:

weakmap.set(key, value);

... ist nur ein Umweg, um dies zu sagen:

key.value = value;

Welche konkreten Anwendungsfälle fehlen mir?

Valderman
quelle
2
Und noch eine - ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6
James Sumners
35
Anwendungsfall in der realen Welt: Speichern Sie benutzerdefinierte Daten für DOM-Knoten.
Felix Kling
Alle Anwendungsfälle, die Sie für schwache Referenzen erwähnen, sind ebenfalls sehr wichtig. Es ist nur viel schwieriger, sie der Sprache hinzuzufügen, da sie den Nichtdeterminismus einführen. Mark Miller und andere haben viel an schwachen Referenzen gearbeitet und ich denke, sie kommen irgendwann. Schließlich
Benjamin Gruenbaum
2
WeakMaps kann verwendet werden, um Speicherlecks zu erkennen: stevehanov.ca/blog/?id=148
theWebalyst

Antworten:

513

Grundsätzlich

WeakMaps bieten eine Möglichkeit, Objekte von außen zu erweitern, ohne die Speicherbereinigung zu beeinträchtigen. Wann immer Sie ein Objekt erweitern möchten, dies aber nicht können, weil es versiegelt ist - oder von einer externen Quelle -, kann eine WeakMap angewendet werden.

Eine WeakMap ist eine Karte (Wörterbuch), in der die Schlüssel schwach sind. Wenn also alle Verweise auf den Schlüssel verloren gehen und keine Verweise mehr auf den Wert vorhanden sind, kann der Wert durch Müll gesammelt werden. Lassen Sie uns dies zuerst anhand von Beispielen zeigen, dann ein wenig erklären und schließlich mit der tatsächlichen Verwendung abschließen.

Angenommen, ich verwende eine API, die mir ein bestimmtes Objekt gibt:

var obj = getObjectFromLibrary();

Jetzt habe ich eine Methode, die das Objekt verwendet:

function useObj(obj){
   doSomethingWith(obj);
}

Ich möchte verfolgen, wie oft die Methode mit einem bestimmten Objekt aufgerufen wurde, und melden, ob es mehr als N Mal vorkommt. Naiv würde man denken, eine Karte zu verwenden:

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

Dies funktioniert, hat jedoch einen Speicherverlust - wir verfolgen jetzt jedes einzelne Bibliotheksobjekt, das an die Funktion übergeben wird, die verhindert, dass die Bibliotheksobjekte jemals durch Müll gesammelt werden. Stattdessen können wir Folgendes verwenden WeakMap:

var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

Und das Speicherleck ist weg.

Anwendungsfälle

Einige Anwendungsfälle, die andernfalls einen Speicherverlust verursachen würden und durch WeakMaps aktiviert werden, umfassen:

  • Behalten Sie private Daten zu einem bestimmten Objekt bei und gewähren Sie nur Personen mit einem Verweis auf die Karte Zugriff darauf. Ein eher ad-hoc-Ansatz kommt mit dem Vorschlag für private Symbole, aber das ist noch eine lange Zeit.
  • Beibehalten von Daten zu Bibliotheksobjekten, ohne diese zu ändern oder Overhead zu verursachen.
  • Speichern von Daten zu einer kleinen Gruppe von Objekten, in denen viele Objekte des Typs vorhanden sind, um keine Probleme mit versteckten Klassen zu verursachen, die JS-Engines für Objekte desselben Typs verwenden.
  • Speichern von Daten zu Hostobjekten wie DOM-Knoten im Browser.
  • Hinzufügen einer Funktion zu einem Objekt von außen (wie im Beispiel für den Ereignisemitter in der anderen Antwort).

Schauen wir uns eine echte Verwendung an

Es kann verwendet werden, um ein Objekt von außen zu erweitern. Lassen Sie uns ein praktisches (angepasstes, reales - um es klar zu machen) Beispiel aus der realen Welt von Node.js geben.

Angenommen, Sie sind Node.js und haben PromiseObjekte. Jetzt möchten Sie alle derzeit abgelehnten Versprechen verfolgen. Sie möchten jedoch nicht verhindern , dass sie als Müll gesammelt werden, falls keine Verweise darauf vorhanden sind.

Jetzt möchten Sie nativen Objekten aus offensichtlichen Gründen keine Eigenschaften hinzufügen - Sie stecken also fest. Wenn Sie Verweise auf die Versprechen einhalten, verursachen Sie einen Speicherverlust, da keine Speicherbereinigung stattfinden kann. Wenn Sie keine Referenzen aufbewahren, können Sie keine zusätzlichen Informationen zu einzelnen Versprechungen speichern. Jedes Schema, bei dem die ID eines Versprechens von Natur aus gespeichert wird, bedeutet, dass Sie einen Verweis darauf benötigen.

Geben Sie WeakMaps ein

WeakMaps bedeuten, dass die Tasten schwach sind. Es gibt keine Möglichkeit, eine schwache Karte aufzuzählen oder alle ihre Werte abzurufen. In einer schwachen Zuordnung können Sie die Daten basierend auf einem Schlüssel speichern. Wenn der Schlüssel Müll sammelt, werden auch die Werte gespeichert.

Dies bedeutet, dass Sie bei einem gegebenen Versprechen den Status darüber speichern können - und dass das Objekt immer noch Müll gesammelt werden kann. Wenn Sie später einen Verweis auf ein Objekt erhalten, können Sie überprüfen, ob Sie einen Status haben, der sich darauf bezieht, und ihn melden.

Dies wurde verwendet , um zu implementieren unhandled Ablehnung Haken von Petka Antonov als dies :

process.on('unhandledRejection', function(reason, p) {
    console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging, throwing an error, or other logic here
});

Wir speichern Informationen zu Versprechungen in einer Karte und können wissen, wann ein abgelehntes Versprechen bearbeitet wurde.

Benjamin Gruenbaum
quelle
8
Hallo! Können Sie mir bitte sagen, welcher Teil des Beispielcodes einen Speicherverlust verursacht?
ltamajs
15
@ ltamajs4 sicher, im useObjBeispiel mit a Mapund nicht a verwenden WeakMapwir das übergebene Objekt als Kartenschlüssel. Das Objekt wird niemals von der Karte entfernt (da wir nicht wissen würden, wann wir das tun sollen), daher gibt es immer einen Verweis darauf und es kann niemals Müll gesammelt werden. Im WeakMap-Beispiel kann das Objekt aus dem gelöscht werden, sobald alle anderen Verweise auf das Objekt verschwunden sind WeakMap. Wenn Sie immer noch nicht sicher sind, was ich meine, lassen Sie es mich bitte wissen
Benjamin Gruenbaum
@Benjamin, Wir müssen zwischen der Notwendigkeit eines speicherempfindlichen Caches und der Notwendigkeit eines data_object-Tupels unterscheiden. Kombinieren Sie diese beiden Anforderungen nicht. Ihr calledBeispiel ist besser mit jsfiddle.net/f2efbm7z geschrieben und zeigt nicht die Verwendung einer schwachen Karte. Tatsächlich kann es auf insgesamt 6 Arten besser geschrieben werden, die ich unten auflisten werde.
Pacerier
Grundsätzlich ist der Zweck einer schwachen Karte ein speicherempfindlicher Cache. Während es verwendet werden kann, um Objekte von außen zu erweitern, ist dies ein unerträglicher mieser Hack und definitiv nicht der richtige Zweck .
Pacerier
1
Wenn Sie die Verbindung zwischen einem Versprechen und der Häufigkeit, mit der es bearbeitet / abgelehnt wurde, beibehalten möchten, verwenden Sie 1) das Symbol; p[key_symbol] = data. oder 2) eindeutige Benennung; p.__key = data. oder 3) privater Geltungsbereich; (()=>{let data; p.Key = _=>data=_;})(). oder 4) Proxy mit 1 oder 2 oder 3. oder 5) Ersetzen / Erweitern der Promise-Klasse durch 1 oder 2 oder 3. oder 6) Ersetzen / Erweitern der Promise-Klasse durch ein Tupel der benötigten Mitglieder. - In jedem Fall wird eine schwache Karte nur benötigt, wenn Sie einen speichersensitiven Cache benötigen.
Pacerier
48

Diese Antwort scheint in einem realen Szenario voreingenommen und unbrauchbar zu sein. Bitte lesen Sie es so wie es ist und betrachten Sie es nicht als tatsächliche Option für etwas anderes als Experimentieren

Ein Anwendungsfall könnte sein, es als Wörterbuch für Hörer zu verwenden. Ich habe einen Kollegen, der das getan hat. Es ist sehr hilfreich, weil jeder Zuhörer direkt auf diese Art und Weise ausgerichtet ist. Auf Wiedersehen listener.on.

Aus abstrakterer Sicht WeakMapist es jedoch besonders leistungsfähig, den Zugriff auf praktisch alles zu entmaterialisieren. Sie benötigen keinen Namespace, um seine Mitglieder zu isolieren, da dies bereits durch die Art dieser Struktur impliziert ist. Ich bin mir ziemlich sicher, dass Sie einige wichtige Speicherverbesserungen vornehmen können, indem Sie awkwards redundante Objektschlüssel ersetzen (obwohl das Dekonstruieren die Arbeit für Sie erledigt).


Bevor Sie lesen, was als nächstes kommt

Ich weiß jetzt , meine betonen ist nicht gerade der beste Weg , um das Problem zu lösen und als Benjamin Grünbaum (check out seine Antwort, wenn es nicht bereits über mir ist: p) wies darauf hin, dieses Problem nicht mit einem regulären gelöst worden sein könnte Map, da es wäre durchgesickert, daher besteht die Hauptstärke WeakMapdarin, dass es die Speicherbereinigung nicht beeinträchtigt, da sie keine Referenz behalten.


Hier ist der aktuelle Code meines Kollegen (danke an ihn für das Teilen)

Vollständige Quelle hier , es geht um das Listener-Management, über das ich oben gesprochen habe (Sie können sich auch die technischen Daten ansehen ).

var listenableMap = new WeakMap();


export function getListenable (object) {
    if (!listenableMap.has(object)) {
        listenableMap.set(object, {});
    }

    return listenableMap.get(object);
}


export function getListeners (object, identifier) {
    var listenable = getListenable(object);
    listenable[identifier] = listenable[identifier] || [];

    return listenable[identifier];
}


export function on (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    listeners.push(listener);
}


export function removeListener (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    var index = listeners.indexOf(listener);
    if(index !== -1) {
        listeners.splice(index, 1);
    }
}


export function emit (object, identifier, ...args) {
    var listeners = getListeners(object, identifier);

    for (var listener of listeners) {
        listener.apply(object, args);
    }
}
Axelduch
quelle
2
Ich verstehe nicht ganz, wie Sie das verwenden würden. Dies würde dazu führen, dass das Observable zusammen mit daran gebundenen Ereignissen zusammenbricht, wenn nicht mehr darauf verwiesen wird. Das Problem, das ich habe, ist, wenn der Beobachter nicht mehr referenziert wird. Ich denke, die Lösung hier hat nur die Hälfte des Problems gelöst. Ich glaube nicht, dass Sie das Beobachterproblem mit WeakMap lösen können, da es nicht iterierbar ist.
jgmjgm
1
Ereignis-Listener mit doppelter Pufferung können in anderen Sprachen schnell sein, in diesem Fall ist sie jedoch einfach esoterisch und langsam. Das sind meine drei Cent.
Jack Giffin
@axelduch, Wow, dieser Mythos vom Hörer-Handle wurde bis in die Javascript-Community verbreitet und hat 40 positive Stimmen erhalten! Um zu verstehen, warum diese Antwort völlig falsch ist , lesen
Pacerier
1
@ Pacerier aktualisiert die Antwort, danke für das Feedback
Axelduch
1
@axelduch, ja, von dort gibt es auch einen Schiedsrichter.
Pacerier
18

WeakMap funktioniert gut für die Kapselung und das Verstecken von Informationen

WeakMapist nur für ES6 und höher verfügbar. A WeakMapist eine Sammlung von Schlüssel- und Wertepaaren, bei denen der Schlüssel ein Objekt sein muss. Im folgenden Beispiel erstellen wir ein WeakMapmit zwei Elementen:

var map = new WeakMap();
var pavloHero = {first: "Pavlo", last: "Hero"};
var gabrielFranco = {first: "Gabriel", last: "Franco"};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero));//This is Hero

Wir haben die set()Methode verwendet, um eine Zuordnung zwischen einem Objekt und einem anderen Element (in unserem Fall eine Zeichenfolge) zu definieren. Wir haben die get()Methode verwendet, um das einem Objekt zugeordnete Element abzurufen. Der interessante Aspekt des WeakMaps ist die Tatsache, dass es einen schwachen Verweis auf den Schlüssel in der Karte enthält. Eine schwache Referenz bedeutet, dass der Garbage Collector bei Zerstörung des Objekts den gesamten Eintrag aus dem entfernt WeakMapund so Speicherplatz freigibt.

var TheatreSeats = (function() {
  var priv = new WeakMap();
  var _ = function(instance) {
    return priv.get(instance);
  };

  return (function() {
      function TheatreSeatsConstructor() {
        var privateMembers = {
          seats: []
        };
        priv.set(this, privateMembers);
        this.maxSize = 10;
      }
      TheatreSeatsConstructor.prototype.placePerson = function(person) {
        _(this).seats.push(person);
      };
      TheatreSeatsConstructor.prototype.countOccupiedSeats = function() {
        return _(this).seats.length;
      };
      TheatreSeatsConstructor.prototype.isSoldOut = function() {
        return _(this).seats.length >= this.maxSize;
      };
      TheatreSeatsConstructor.prototype.countFreeSeats = function() {
        return this.maxSize - _(this).seats.length;
      };
      return TheatreSeatsConstructor;
    }());
})()
Michael Horojanski
quelle
4
Zu "Schwachkarten eignen sich gut zum Einkapseln und Verstecken von Informationen". Nur weil du kannst, heißt das nicht, dass du es solltest. Javascript verfügt über Standardmethoden zum Einkapseln und Ausblenden von Informationen, noch bevor die Schwachkarte erfunden wurde. Derzeit gibt es buchstäblich 6 Möglichkeiten, dies zu tun . Die Verwendung von Schwachkarten zur Kapselung ist eine hässliche Gesichtspalme.
Pacerier
12

𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮

Schwache Karten können verwendet werden, um Metadaten zu DOM-Elementen zu speichern, ohne die Speicherbereinigung zu beeinträchtigen oder Kollegen auf Ihren Code zu verärgern. Sie können sie beispielsweise verwenden, um alle Elemente einer Webseite numerisch zu indizieren.

𝗪𝗶𝘁𝗵𝗼𝘂𝘁 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗼𝗿 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:

var elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length;

while (++i !== len) {
  // Production code written this poorly makes me want to cry:
  elements[i].lookupindex = i;
  elements[i].elementref = [];
  elements[i].elementref.push( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
// For those of you new to javascirpt, I hope the comments below help explain 
// how the ternary operator (?:) works like an inline if-statement
document.write(document.body.lookupindex + '<br />' + (
    (document.body.elementref.indexOf(document.currentScript) !== -1)
    ? // if(document.body.elementref.indexOf(document.currentScript) !== -1){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗨𝘀𝗶𝗻𝗴 𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀 𝗮𝗻𝗱 𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:

var DOMref = new WeakMap(),
  __DOMref_value = Array,
  __DOMref_lookupindex = 0,
  __DOMref_otherelement = 1,
  elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length, cur;

while (++i !== len) {
  // Production code written this greatly makes me want to 😊:
  cur = DOMref.get(elements[i]);
  if (cur === undefined)
    DOMref.set(elements[i], cur = new __DOMref_value)

  cur[__DOMref_lookupindex] = i;
  cur[__DOMref_otherelement] = new WeakSet();
  cur[__DOMref_otherelement].add( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
cur = DOMref.get(document.body)
document.write(cur[__DOMref_lookupindex] + '<br />' + (
    cur[__DOMref_otherelement].has(document.currentScript)
    ? // if(cur[__DOMref_otherelement].has(document.currentScript)){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗧𝗵𝗲 𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲

Der Unterschied mag vernachlässigbar erscheinen, abgesehen von der Tatsache, dass die Schwachkartenversion länger ist, es gibt jedoch einen großen Unterschied zwischen den beiden oben gezeigten Codeteilen. Im ersten Codeausschnitt ohne schwache Karten speichert der Codeteil Referenzen in jeder Richtung zwischen den DOM-Elementen. Dies verhindert, dass die DOM-Elemente durch Müll gesammelt werden.(i * i) % lenmag wie ein seltsamer Ball erscheinen, den niemand verwenden würde, aber denken Sie noch einmal darüber nach: Viele Produktionscodes enthalten DOM-Referenzen, die über das gesamte Dokument verteilt sind. Für den zweiten Codeteil kann der Browser beim Entfernen eines Knotens feststellen, dass der Knoten nicht verwendet wird (von Ihrem Code nicht erreichbar), da alle Verweise auf die Elemente schwach sind Löschen Sie es daher aus dem Speicher. Der Grund, warum Sie sich Gedanken über die Speichernutzung und Speicheranker machen sollten (z. B. das erste Codeausschnitt, in dem nicht verwendete Elemente im Speicher gespeichert sind), liegt darin, dass eine höhere Speichernutzung mehr Browser-GC-Versuche bedeutet (um zu versuchen, Speicher freizugeben) Abwenden eines Browserabsturzes) bedeutet langsameres Surferlebnis und manchmal einen Browserabsturz.

Für eine Polyfüllung für diese würde ich meine eigene Bibliothek empfehlen ( hier @ github zu finden ). Es ist eine sehr leichte Bibliothek, die sie einfach ohne die viel zu komplexen Frameworks, die Sie in anderen Polyfills finden, polyfill.

~ Viel Spaß beim Codieren!

Jack Giffin
quelle
1
Danke für die klare Erklärung. Ein Beispiel ist mehr wert als Worte.
Newguy
@lolzery, Re " Dies verhindert, dass die DOM-Elemente durch Müll gesammelt werden ". Alles, was Sie brauchen, ist auf null zu setzenelements und fertig: Es wird GCed. & Re " DOM-Referenzen, die im gesamten Dokument abprallen ", spielt keine Rolle: Sobald der Hauptlink entfernt elementsist, werden alle Rundschreiben-Refs GCed. Wenn Ihr Element Verweise auf Elemente enthält, die es nicht benötigt, korrigieren Sie den Code und setzen Sie den Verweis auf null, wenn Sie mit der Verwendung fertig sind. Es wird GCed. Schwachkarten werden nicht benötigt .
Pacerier
2
@Pacerier Vielen Dank für Ihr begeistertes Feedback. Wenn Sie jedoch den Wert elementsauf Null setzen , kann der Browser die Elemente in der ersten Snippet-Situation nicht überprüfen . Dies liegt daran, dass Sie benutzerdefinierte Eigenschaften für die Elemente festlegen und diese Elemente dann weiterhin abgerufen werden können und auf ihre benutzerdefinierten Eigenschaften weiterhin zugegriffen werden kann, wodurch verhindert wird, dass eines von ihnen GC-geprüft wird. Stellen Sie es sich wie eine Kette von Metallringen vor. Wenn Sie Zugriff auf mindestens ein Glied in der Kette haben, können Sie dieses Glied in der Kette festhalten und so verhindern, dass die gesamte Kette von Gegenständen in den Abgrund fällt.
Jack Giffin
1
Produktionscode mit Dunder namens Vars bringt mich zum Erbrechen
Barbu Barbu
10

Ich verwende WeakMapfür den Cache die sorgenfreie Speicherung von Funktionen, die unveränderliche Objekte als Parameter verwenden.

Das Auswendiglernen ist eine ausgefallene Art zu sagen: "Nachdem Sie den Wert berechnet haben, speichern Sie ihn im Cache, damit Sie ihn nicht erneut berechnen müssen."

Hier ist ein Beispiel:

Ein paar Dinge zu beachten:

  • Immutable.js-Objekte geben neue Objekte (mit einem neuen Zeiger) zurück, wenn Sie sie ändern. Wenn Sie sie also als Schlüssel in einer WeakMap verwenden, wird der gleiche berechnete Wert garantiert.
  • Die WeakMap eignet sich hervorragend für Memos, da der berechnete Wert auf der WeakMap auch dann erfasst wird, wenn das Objekt (das als Schlüssel verwendet wird) den Müll sammelt.
Rico Kahler
quelle
1
Dies ist eine gültige Verwendung von Schwachkarten, solange der Memo-Cache speicherempfindlich sein soll und nicht während der gesamten Lebensdauer des Objekts / der Funktion bestehen bleibt. Wenn der "Memoization Cache" während der gesamten Lebensdauer des Objekts / der Funktion dauerhaft sein soll, ist eine Schwachkarte die falsche Wahl: Verwenden Sie stattdessen eine der 6 Standard-Javascript-Kapselungstechniken .
Pacerier
3

Ich habe diesen einfachen funktionsbasierten Anwendungsfall / Beispiel für WeakMaps.

Verwalten Sie eine Sammlung von Benutzern

Ich begann mit einem off - UserObjekt , deren Eigenschaften umfassen ein fullname, username, age, genderund ein Verfahren genannt , printdas eines vom Menschen lesbare Zusammenfassung der anderen Eigenschaften druckt.

/**
Basic User Object with common properties.
*/
function User(username, fullname, age, gender) {
    this.username = username;
    this.fullname = fullname;
    this.age = age;
    this.gender = gender;
    this.print = () => console.log(`${this.fullname} is a ${age} year old ${gender}`);
}

Ich habe dann eine Map hinzugefügt, die aufgerufen wird users, um eine Sammlung mehrerer Benutzer zu speichern, die von verschlüsselt sind username.

/**
Collection of Users, keyed by username.
*/
var users = new Map();

Das Hinzufügen der Sammlung erforderte außerdem Hilfsfunktionen zum Hinzufügen, Abrufen, Löschen eines Benutzers und sogar eine Funktion zum Drucken aller Benutzer der Vollständigkeit halber.

/**
Creates an User Object and adds it to the users Collection.
*/
var addUser = (username, fullname, age, gender) => {
    let an_user = new User(username, fullname, age, gender);
    users.set(username, an_user);
}

/**
Returns an User Object associated with the given username in the Collection.
*/
var getUser = (username) => {
    return users.get(username);
}

/**
Deletes an User Object associated with the given username in the Collection.
*/
var deleteUser = (username) => {
    users.delete(username);
}

/**
Prints summary of all the User Objects in the Collection.
*/
var printUsers = () => {
    users.forEach((user) => {
        user.print();
    });
}

Wennusers der gesamte oben genannte Code beispielsweise in NodeJS ausgeführt wird , enthält nur die Map den Verweis auf die Benutzerobjekte innerhalb des gesamten Prozesses. Es gibt keinen weiteren Verweis auf die einzelnen Benutzerobjekte.

Wenn Sie diesen Code als interaktive NodeJS-Shell ausführen, füge ich als Beispiel vier Benutzer hinzu und drucke sie aus: Benutzer hinzufügen und drucken

Fügen Sie Benutzern weitere Informationen hinzu, ohne den vorhandenen Code zu ändern

Angenommen, es ist eine neue Funktion erforderlich, bei der die SMP-Links (Social Media Platform) jedes Benutzers zusammen mit den Benutzerobjekten verfolgt werden müssen.

Der Schlüssel hier ist auch, dass diese Funktion mit minimalem Eingriff in den vorhandenen Code implementiert werden muss.

Dies ist mit WeakMaps folgendermaßen möglich.

Ich füge drei separate WeakMaps für Twitter, Facebook, LinkedIn hinzu.

/*
WeakMaps for Social Media Platforms (SMPs).
Could be replaced by a single Map which can grow
dynamically based on different SMP names . . . anyway...
*/
var sm_platform_twitter = new WeakMap();
var sm_platform_facebook = new WeakMap();
var sm_platform_linkedin = new WeakMap();

Eine getSMPWeakMapHilfsfunktion wird einfach hinzugefügt, um die WeakMap zurückzugeben, die dem angegebenen SMP-Namen zugeordnet ist.

/**
Returns the WeakMap for the given SMP.
*/
var getSMPWeakMap = (sm_platform) => {
    if(sm_platform == "Twitter") {
        return sm_platform_twitter;
    }
    else if(sm_platform == "Facebook") {
        return sm_platform_facebook;
    }
    else if(sm_platform == "LinkedIn") {
        return sm_platform_linkedin;
    }
    return undefined;
}

Eine Funktion zum Hinzufügen eines Benutzer-SMP-Links zur angegebenen SMP-Schwachkarte.

/**
Adds a SMP link associated with a given User. The User must be already added to the Collection.
*/
var addUserSocialMediaLink = (username, sm_platform, sm_link) => {
    let user = getUser(username);
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    if(user && sm_platform_weakmap) {
        sm_platform_weakmap.set(user, sm_link);
    }
}

Eine Funktion zum Drucken nur der Benutzer, die auf dem angegebenen SMP vorhanden sind.

/**
Prints the User's fullname and corresponding SMP link of only those Users which are on the given SMP.
*/
var printSMPUsers = (sm_platform) => {
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    console.log(`Users of ${sm_platform}:`)
    users.forEach((user)=>{
        if(sm_platform_weakmap.has(user)) {
            console.log(`\t${user.fullname} : ${sm_platform_weakmap.get(user)}`)
        }
    });
}

Sie können jetzt SMP-Links für die Benutzer hinzufügen, auch mit der Möglichkeit, dass jeder Benutzer einen Link auf mehreren SMPs hat.

... Fortsetzung des vorherigen Beispiels: Ich füge den Benutzern SMP-Links hinzu, mehrere Links für die Benutzer Bill und Sarah, und drucke dann die Links für jedes SMP separat aus: Hinzufügen von SMP-Links zu den Benutzern und Anzeigen dieser

Angenommen, ein Benutzer wird usersdurch einen Anruf aus der Karte gelöscht deleteUser. Dadurch wird der einzige Verweis auf das Benutzerobjekt entfernt. Dadurch wird auch die SMP-Verbindung von allen SMP WeakMaps (von Garbage Collection) gelöscht, da ohne das Benutzerobjekt keine Möglichkeit besteht, auf eine der SMP-Verbindungen zuzugreifen.

... Wenn ich mit dem Beispiel fortfahre, lösche ich Benutzer Bill und drucke dann die Links der SMPs aus, mit denen er verbunden war:

Durch das Löschen der Benutzerrechnung aus der Karte werden auch die SMP-Links entfernt

Es ist kein zusätzlicher Code erforderlich, um den SMP-Link einzeln und den vorhandenen Code einzeln zu löschen, bevor diese Funktion ohnehin nicht geändert wurde.

Wenn es eine andere Möglichkeit gibt, diese Funktion mit / ohne WeakMaps hinzuzufügen, können Sie dies gerne kommentieren.

Elektrokrat
quelle
_____nice______
Aleks