Quali sono gli utilizzi effettivi di ES6 WeakMap?


397

Quali sono gli usi effettivi della WeakMapstruttura dei dati introdotta in ECMAScript 6?

Poiché una chiave di una mappa debole crea un forte riferimento al suo valore corrispondente, assicurando che un valore che è stato inserito in una mappa debole non scomparirà mai fintanto che la sua chiave è ancora in vita, non può essere utilizzato per le tabelle dei memo, cache o qualsiasi altra cosa per cui normalmente utilizzereste riferimenti deboli, mappe con valori deboli, ecc.

Mi sembra che questo:

weakmap.set(key, value);

... è solo un modo per dirlo:

key.value = value;

Quali casi d'uso concreti mi sto perdendo?




35
Caso d'uso reale: archivia i dati personalizzati per i nodi DOM.
Felix Kling,

Tutti i casi d'uso citati per riferimenti deboli sono anche molto importanti. Sono solo molto più difficili da aggiungere alla lingua poiché introducono il non determinismo. Mark Miller e altri hanno lavorato molto su riferimenti deboli e penso che alla fine arriveranno. Alla fine
Benjamin Gruenbaum,

2
WeakMaps può essere usato per rilevare perdite di memoria: stevehanov.ca/blog/?id=148
theWebalyst

Risposte:


513

fondamentalmente

WeakMaps offre un modo per estendere gli oggetti dall'esterno senza interferire con la garbage collection. Ogni volta che vuoi estendere un oggetto ma non puoi perché è sigillato - o da una fonte esterna - puoi applicare una WeakMap.

Una WeakMap è una mappa (dizionario) in cui le chiavi sono deboli, ovvero se tutti i riferimenti alla chiave vengono persi e non ci sono più riferimenti al valore, il valore può essere garbage collection. Mostriamo prima questo attraverso esempi, quindi spieghiamo un po 'e infine finiamo con un uso reale.

Diciamo che sto usando un'API che mi dà un certo oggetto:

var obj = getObjectFromLibrary();

Ora, ho un metodo che utilizza l'oggetto:

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

Voglio tenere traccia di quante volte il metodo è stato chiamato con un determinato oggetto e segnalare se succede più di N volte. Ingenuamente si potrebbe pensare di usare una mappa:

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);
}

Funziona, ma ha una perdita di memoria: ora teniamo traccia di ogni singolo oggetto della libreria passato alla funzione che impedisce agli oggetti della libreria di essere mai raccolti. Invece - possiamo usare un 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);
}

E la perdita di memoria è sparita.

Casi d'uso

Alcuni casi d'uso che altrimenti causerebbero una perdita di memoria e che sono abilitati da WeakMaps includono:

  • Mantenere i dati privati ​​su un oggetto specifico e dare accesso ad esso solo alle persone con un riferimento alla Mappa. Un approccio più ad-hoc sta arrivando con la proposta di simboli privati, ma è molto tempo da ora.
  • Conservazione dei dati sugli oggetti della libreria senza modificarli o incorrere in spese generali.
  • Mantenere i dati su un piccolo insieme di oggetti in cui esistono molti oggetti del tipo per non incorrere in problemi con le classi nascoste che i motori JS usano per oggetti dello stesso tipo.
  • Conservazione dei dati su oggetti host come nodi DOM nel browser.
  • Aggiunta di una capacità a un oggetto dall'esterno (come nell'esempio dell'emettitore di eventi nell'altra risposta).

Diamo un'occhiata a un uso reale

Può essere usato per estendere un oggetto dall'esterno. Diamo un esempio pratico (adattato, una specie di reale - per fare un punto) dal mondo reale di Node.js.

Supponiamo che tu sia Node.js e che tu abbia degli Promiseoggetti - ora vuoi tenere traccia di tutte le promesse attualmente respinte - tuttavia, non vuoi impedire che vengano spazzate via nel caso in cui non esistano riferimenti.

Ora, non vuoi aggiungere proprietà agli oggetti nativi per ovvi motivi, quindi sei bloccato. Se mantieni i riferimenti alle promesse stai causando una perdita di memoria poiché non può verificarsi la raccolta dei rifiuti. Se non conservi i riferimenti, non puoi salvare ulteriori informazioni sulle singole promesse. Qualsiasi schema che comporta il salvataggio dell'ID di una promessa implica intrinsecamente che è necessario un riferimento ad essa.

Inserisci WeakMaps

WeakMaps significa che i tasti sono deboli. Non ci sono modi per enumerare una mappa debole o per ottenere tutti i suoi valori. In una mappa debole, è possibile archiviare i dati in base a una chiave e quando la chiave viene raccolta in modo errato, fare altrettanto con i valori.

Ciò significa che, data la promessa, è possibile memorizzare lo stato al riguardo - e quell'oggetto può ancora essere raccolto. In seguito, se si ottiene un riferimento a un oggetto, è possibile verificare se si dispone di uno stato relativo ad esso e segnalarlo.

Questo è stato usato per implementare ganci di rifiuto non gestiti da Petka Antonov come questo :

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
});

Manteniamo le informazioni sulle promesse in una mappa e possiamo sapere quando è stata gestita una promessa respinta.


8
Ciao! Potresti dirmi quale parte del codice di esempio causa la perdita di memoria?
ltamajs,

15
@ltamajs4 certo, useObjnell'esempio usando a Mape non a WeakMapusiamo l'oggetto passato come chiave della mappa. L'oggetto non viene mai rimosso dalla mappa (dal momento che non sapremmo quando farlo), quindi c'è sempre un riferimento ad esso e non può mai essere spazzatura. Nell'esempio di WeakMap non appena tutti gli altri riferimenti all'oggetto scompaiono, l'oggetto può essere cancellato da WeakMap. Se non sei ancora sicuro di cosa intendo, per favore fatemelo sapere
Benjamin Gruenbaum,

@Benjamin, Dobbiamo distinguere tra la necessità di una cache sensibile alla memoria e la necessità di una tupla data_object. Non confondere questi due requisiti separati. Il tuo calledesempio è meglio scritto usando jsfiddle.net/f2efbm7z e non dimostra l'uso di una mappa debole. In effetti, può essere scritto meglio in 6 modi, che elencherò di seguito.
Pacerier,

Fondamentalmente, lo scopo della mappa debole è una cache sensibile alla memoria. Mentre può essere usato per estendere gli oggetti dall'esterno, questo è un pessimo hack non trattato e non è sicuramente il suo scopo .
Pacerier,

1
Se si desidera mantenere il collegamento tra una promessa e il numero di volte in cui è stata gestita / rifiutata, utilizzare il simbolo 1) ; p[key_symbol] = data. o 2) denominazione unica; p.__key = data. o 3) ambito privato; (()=>{let data; p.Key = _=>data=_;})(). oppure 4) proxy con 1 o 2 o 3. o 5) sostituzione / estensione della classe Promise con 1 o 2 o 3. o 6) sostituzione / estensione della classe Promise con una tupla di membri necessari. - In ogni caso, non è necessaria una mappa debole a meno che non sia necessaria una cache sensibile alla memoria.
Pacerier,

48

Questa risposta sembra essere parziale e inservibile in uno scenario del mondo reale. Si prega di leggere così com'è e non considerarlo come un'opzione effettiva per nient'altro che la sperimentazione

Un caso d'uso potrebbe essere quello di usarlo come dizionario per gli ascoltatori, ho un collega che lo ha fatto. È molto utile perché qualsiasi ascoltatore è indirizzato direttamente a questo modo di fare le cose. Arrivederci listener.on.

Ma da un punto di vista più astratto, WeakMapè particolarmente potente per smaterializzare l'accesso a praticamente qualsiasi cosa, non è necessario uno spazio dei nomi per isolare i suoi membri poiché è già implicito dalla natura di questa struttura. Sono abbastanza sicuro che potresti apportare alcuni importanti miglioramenti alla memoria sostituendo le chiavi di oggetto ridondanti scomode (anche se la decostruzione fa il lavoro per te).


Prima di leggere quello che è il prossimo

Ora mi rendo conto che la mia enfasi non è esattamente il modo migliore per affrontare il problema e come ha sottolineato Benjamin Gruenbaum (controlla la sua risposta, se non è già al di sopra della mia: p), questo problema non avrebbe potuto essere risolto con un regolare Map, dal momento che sarebbe trapelato, quindi il principale punto di forza WeakMapè che non interferisce con la raccolta dei rifiuti dato che non mantengono un riferimento.


Ecco il vero codice del mio collega (grazie a lui per la condivisione)

Fonte completa qui , riguarda la gestione degli ascoltatori di cui ho parlato sopra (puoi anche dare un'occhiata alle specifiche )

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);
    }
}

2
Non capisco come lo useresti. Provocherebbe il collasso dell'osservabile insieme agli eventi ad esso associati quando non sono più citati. Il problema che tendo ad avere è quando all'Observer non viene più fatto riferimento. Penso che la soluzione qui abbia risolto solo metà del problema. Non penso che tu possa risolvere il problema dell'osservatore con WeakMap in quanto non è iterabile.
jgmjgm,

1
I listener di eventi con doppio buffering possono essere veloci in altre lingue, ma in questo caso è semplicemente esoterico e lento. Sono i miei tre centesimi.
Jack Giffin,

@axelduch, Wow questo mito dell'ascoltatore è stato distribuito fino alla comunità Javascript, ottenendo 40 voti! Per capire perché questa risposta è completamente sbagliato , vedere i commenti sotto stackoverflow.com/a/156618/632951
Pacerier

1
@Pacerier ha aggiornato la risposta, grazie per il feedback
axelduch

1
@axelduch, sì, c'è anche un riferimento da lì.
Pacerier,

18

WeakMap funziona bene per incapsulamento e nascondere informazioni

WeakMapè disponibile solo per ES6 e versioni successive. A WeakMapè una raccolta di coppie chiave e valore in cui la chiave deve essere un oggetto. Nel seguente esempio, costruiamo un WeakMapcon due elementi:

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

Abbiamo usato il set()metodo per definire un'associazione tra un oggetto e un altro elemento (una stringa nel nostro caso). Abbiamo utilizzato il get()metodo per recuperare l'elemento associato a un oggetto. L'aspetto interessante della WeakMaps è il fatto che contiene un debole riferimento alla chiave all'interno della mappa. Un riferimento debole significa che se l'oggetto viene distrutto, il Garbage Collector rimuoverà l'intera voce dalla WeakMap, liberando così la memoria.

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;
    }());
})()

4
Ri "mappa debole funziona bene per l'incapsulamento e il nascondimento delle informazioni". Solo perché puoi, non significa che dovresti. Javascript ha modi predefiniti di incapsulare e nascondere le informazioni anche prima che fosse inventata la mappa debole. Al momento, ci sono letteralmente 6 modi per farlo . Usare la mappa debole per fare l'incapsulamento è una brutta faccia.
Pacerier,

12

𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮

Le mappe deboli possono essere utilizzate per archiviare metadati sugli elementi DOM senza interferire con la garbage collection o far impazzire i colleghi con il tuo codice. Ad esempio, è possibile utilizzarli per indicizzare numericamente tutti gli elementi in una pagina Web.

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

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"
  )   // }
);

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

La differenza può sembrare trascurabile, a parte il fatto che la versione dimap debole è più lunga, tuttavia c'è una grande differenza tra i due pezzi di codice mostrati sopra. Nel primo frammento di codice, senza mappe deboli, il pezzo di codice memorizza i riferimenti in ogni modo tra gli elementi DOM. Ciò impedisce che gli elementi DOM vengano raccolti.(i * i) % lenpuò sembrare una strana palla che nessuno userebbe, ma ripensaci: un sacco di codice di produzione ha riferimenti DOM che rimbalzano su tutto il documento. Ora, per il secondo pezzo di codice, poiché tutti i riferimenti agli elementi sono deboli, quando si rimuove un nodo, il browser è in grado di determinare che il nodo non è utilizzato (impossibile da raggiungere dal proprio codice), e quindi cancellalo dalla memoria. Il motivo per cui dovresti preoccuparti dell'utilizzo della memoria e degli ancoraggi di memoria (cose come il primo frammento di codice in cui gli elementi inutilizzati sono conservati in memoria) è perché un uso maggiore della memoria significa più tentativi GC del browser (per provare a liberare memoria evitare un arresto del browser) significa un'esperienza di navigazione più lenta e talvolta un arresto del browser.

Per quanto riguarda un polyfill per questi, consiglierei la mia biblioteca ( trovata qui @ github ). È una libreria molto leggera che la riempirà semplicemente senza alcuno dei quadri eccessivamente complessi che potresti trovare in altri polyfill.

~ Buona codifica!


1
Grazie per la chiara spiegazione. Un esempio vale più di qualsiasi parola.
Newguy

@lolzery, Re " Questo impedisce che gli elementi DOM vengano raccolti ", tutto ciò che serve è impostare elementssu null e il gioco è fatto: sarà GCed. & Re " Riferimenti DOM che rimbalzano su tutto il documento ", non importa affatto: una volta che il collegamento principale elementsè andato, tutti i riferimenti circolari saranno GC. Se il tuo elemento sta trattenendo riferimenti a elementi di cui non ha bisogno, correggi il codice e imposta ref su null quando hai finito di usarlo. Sarà GCed. Mappe deboli non necessarie .
Pacerier,

2
@Pacerier grazie per il tuo feedback entusiasta, tuttavia l'impostazione elementssu null non consentirà al browser di GC gli elementi nella prima situazione dello snippet. Questo perché si impostano proprietà personalizzate sugli elementi, quindi è ancora possibile ottenere quegli elementi e si può ancora accedere alle loro proprietà personalizzate, impedendo così a ciascuno di essi di essere GC. Pensalo come una catena di anelli di metallo. Solongas hai accesso ad almeno un anello della catena, puoi trattenerlo e impedire così che l'intera catena di oggetti cada nell'abisso.
Jack Giffin,

1
il codice di produzione con dunder chiamato vars mi fa vomitare
Barbu Barbu,

10

Uso WeakMapper la cache di memoization senza preoccupazioni di funzioni che prendono come oggetto parametri immutabili.

La memorizzazione è un modo elegante di dire "dopo aver calcolato il valore, memorizzalo nella cache in modo da non doverlo calcolare nuovamente".

Ecco un esempio:

Alcune cose da notare:

  • Gli oggetti Immutable.js restituiscono nuovi oggetti (con un nuovo puntatore) quando li modifichi, quindi usarli come chiavi in ​​una WeakMap garantisce lo stesso valore calcolato.
  • WeakMap è ottimo per i memo perché una volta che l'oggetto (usato come chiave) viene raccolto in modo inutile, lo stesso vale per il valore calcolato su WeakMap.

1
Questo è un uso valido di weakmap fintanto che la cache di memoization è pensata per essere sensibile alla memoria , non persistente per tutta la vita di obj / function. Se la "cache di memoization" è pensata per essere persistente per tutta la durata di obj / function, allora debole mappa è la scelta sbagliata: utilizzare invece una delle 6 tecniche di incapsulamento javascript predefinite .
Pacerier,

3

Ho questo semplice caso d'uso / esempio basato su funzionalità per WeakMaps.

GESTISCI UNA COLLEZIONE DI UTENTI

Ho iniziato con un Useroggetto le cui proprietà comprendono una fullname, username, age, gendere un metodo chiamato printche stampa un riassunto leggibile delle altre proprietà.

/**
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}`);
}

Ho quindi aggiunto una mappa chiamata usersper mantenere una raccolta di più utenti su cui sono codificati username.

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

L'aggiunta della Collezione richiedeva anche funzioni di supporto per aggiungere, ottenere, eliminare un Utente e persino una funzione per stampare tutti gli utenti per completezza.

/**
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();
    });
}

Con tutto il codice sopra in esecuzione, diciamo NodeJS , solo la usersmappa ha il riferimento agli oggetti utente all'interno dell'intero processo. Non ci sono altri riferimenti ai singoli oggetti utente.

Eseguendo questo codice una shell NodeJS interattiva, proprio come un esempio, aggiungo quattro utenti e li stampa: Aggiunta e stampa di utenti

AGGIUNGI ALTRE INFORMAZIONI AGLI UTENTI SENZA MODIFICARE IL CODICE ESISTENTE

Ora supponiamo che sia necessaria una nuova funzionalità in cui i collegamenti di Social Media Platform (SMP) di ciascun utente devono essere tracciati insieme agli Oggetti utente.

La chiave qui è anche che questa funzione deve essere implementata con un intervento minimo al codice esistente.

Questo è possibile con WeakMaps nel modo seguente.

Aggiungo tre WeakMaps separate per Twitter, Facebook, LinkedIn.

/*
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();

Una funzione di supporto getSMPWeakMapviene aggiunta semplicemente per restituire la WeakMap associata al nome SMP specificato.

/**
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;
}

Una funzione per aggiungere un collegamento SMP degli utenti alla WeakMap SMP fornita.

/**
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);
    }
}

Una funzione per stampare solo gli utenti presenti sul dato SMP.

/**
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)}`)
        }
    });
}

È ora possibile aggiungere collegamenti SMP per gli utenti, anche con la possibilità che ciascun utente disponga di un collegamento su più SMP.

... continuando con l'esempio precedente, aggiungo i collegamenti SMP agli utenti, più collegamenti per gli utenti Bill e Sarah e quindi stampo i collegamenti per ciascun SMP separatamente: Aggiunta di collegamenti SMP agli utenti e loro visualizzazione

Ora supponiamo che un utente venga eliminato dalla usersmappa chiamando deleteUser. Ciò rimuove l'unico riferimento all'oggetto utente. Questo a sua volta cancellerà anche il collegamento SMP da qualsiasi / tutte le WeakMaps SMP (di Garbage Collection) poiché senza l'oggetto utente non è possibile accedere a nessuno dei suoi collegamenti SMP.

... continuando con l'esempio, elimino l'utente Bill e quindi stampo i collegamenti degli SMP a cui era associato:

L'eliminazione della fattura dell'utente dalla mappa rimuove anche i collegamenti SMP

Non è richiesto alcun codice aggiuntivo per eliminare singolarmente il collegamento SMP separatamente e il codice esistente prima che questa funzione non sia stata modificata in alcun modo.

Se esiste un altro modo per aggiungere questa funzione con / senza WeakMaps, non esitate a commentare.


_____nice______
Aleks
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.