Jakie są faktyczne zastosowania ES6 WeakMap?

397

Jakie są faktyczne zastosowania WeakMapstruktury danych wprowadzonej w ECMAScript 6?

Ponieważ klucz słabej mapy tworzy silne odniesienie do odpowiadającej jej wartości, dlatego wartość, która została wstawiona do słabej mapy, nigdy nie zniknie, dopóki klucz będzie żył, dlatego nie można jej używać w tabelach notatek, pamięci podręczne lub cokolwiek innego, do czego normalnie używasz słabych referencji, map o słabych wartościach itp.

Wydaje mi się, że to:

weakmap.set(key, value);

... to tylko ogólny sposób na powiedzenie tego:

key.value = value;

Jakich konkretnych przypadków użycia brakuje mi?

valderman
źródło
10
Post na blogu.
Pointy
2
I jeszcze jeden - ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6
James Sumners
35
Przypadek użycia w świecie rzeczywistym: przechowuj niestandardowe dane dla węzłów DOM.
Felix Kling
Wszystkie przypadki użycia, o których wspominasz w przypadku słabych referencji, są bardzo ważne. Są o wiele trudniejsze do dodania do języka, ponieważ wprowadzają niedeterminizm. Mark Miller i inni wykonali wiele pracy nad słabymi referencjami i myślę, że w końcu nadejdą. W końcu
Benjamin Gruenbaum
2
WeakMapS może być stosowany do wykrywania wycieków pamięci: stevehanov.ca/blog/?id=148
theWebalyst

Odpowiedzi:

513

Zasadniczo

WeakMaps umożliwiają rozszerzenie obiektów z zewnątrz bez ingerencji w wyrzucanie elementów bezużytecznych. Kiedykolwiek chcesz rozszerzyć obiekt, ale nie możesz, ponieważ jest on zapieczętowany - lub z zewnętrznego źródła - można zastosować WeakMap.

WeakMap to mapa (słownik), w której klucze są słabe - to znaczy, jeśli wszystkie odniesienia do klucza zostaną utracone i nie będzie już żadnych odwołań do wartości - wartość można wyrzucić. Pokażmy to najpierw za pomocą przykładów, następnie wyjaśnijmy to trochę i na koniec zakończmy prawdziwym użyciem.

Powiedzmy, że używam interfejsu API, który daje mi określony obiekt:

var obj = getObjectFromLibrary();

Teraz mam metodę, która używa obiektu:

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

Chcę śledzić, ile razy metoda została wywołana z określonym obiektem i zgłosić, czy zdarzy się to więcej niż N razy. Naiwnie można by pomyśleć o użyciu mapy:

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

Działa to, ale ma przeciek pamięci - śledzimy teraz każdy pojedynczy obiekt biblioteki przekazywany do funkcji, która chroni obiekty biblioteki przed zbieraniem śmieci. Zamiast tego - możemy użyć 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);
}

A przeciek pamięci zniknął.

Przypadków użycia

Niektóre przypadki użycia, które w przeciwnym razie spowodowałyby wyciek pamięci i są włączone przez WeakMaps obejmują:

  • Przechowywanie prywatnych danych o konkretnym obiekcie i udostępnianie go tylko osobom z odniesieniem do mapy. Bardziej ad-hoc pojawia się z propozycją prywatnych symboli, ale to już dawno.
  • Przechowywanie danych o obiektach bibliotecznych bez ich zmieniania lub nakładania kosztów ogólnych.
  • Przechowywanie danych o niewielkim zestawie obiektów, w których istnieje wiele obiektów tego typu, aby nie powodować problemów z ukrytymi klasami używanymi przez silniki JS dla obiektów tego samego typu.
  • Przechowywanie danych o obiektach hosta, takich jak węzły DOM w przeglądarce.
  • Dodanie możliwości do obiektu z zewnątrz (jak przykład emitera zdarzeń w drugiej odpowiedzi).

Spójrzmy na prawdziwe zastosowanie

Można go użyć do przedłużenia obiektu z zewnątrz. Podajmy praktyczny (dostosowany, w pewnym sensie prawdziwy - żeby podkreślić) przykład z prawdziwego świata Node.js.

Załóżmy, że jesteś Node.js i masz Promiseobiekty - teraz chcesz śledzić wszystkie obecnie odrzucone obietnice - jednak nie chcesz, aby nie były one usuwane w przypadku, gdy nie ma żadnych odwołań do nich.

Teraz nie chcesz dodawać właściwości do obiektów rodzimych z oczywistych powodów - więc utkniesz. Jeśli zachowasz odniesienia do obietnic, spowodujesz wyciek pamięci, ponieważ nie będzie możliwe wyrzucanie elementów bezużytecznych. Jeśli nie przechowujesz referencji, nie możesz zapisać dodatkowych informacji o poszczególnych obietnicach. Każdy schemat, który wymaga zapisania identyfikatora obietnicy z natury oznacza, że ​​potrzebujesz odniesienia do niego.

Wpisz WeakMaps

WeakMaps oznacza, że klucze są słabe. Nie ma możliwości wyliczenia słabej mapy ani uzyskania wszystkich jej wartości. Na słabej mapie możesz przechowywać dane w oparciu o klucz, a kiedy klucz zostanie wyrzucony, usuwaj wartości.

Oznacza to, że biorąc pod uwagę obietnicę, możesz przechowywać stan na ten temat - i ten obiekt może być nadal usuwany. Później, jeśli otrzymasz odniesienie do obiektu, możesz sprawdzić, czy masz do niego jakiś stan i zgłosić go.

Zostało to wykorzystane do realizacji nieobsłużonych haki odrzucenia przez Petka Antonov jak to :

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

Przechowujemy informacje o obietnicach na mapie i możemy wiedzieć, kiedy zrealizowano odrzuconą obietnicę.

Benjamin Gruenbaum
źródło
8
Dzień dobry! Czy możesz mi powiedzieć, która część przykładowego kodu powoduje wyciek pamięci?
ltamajs
15
@ ltamajs4 na pewno, w useObjprzykładzie używającym a, Mapa nie a WeakMap, używamy przekazanego obiektu jako klucza mapy. Obiekt nigdy nie jest usuwany z mapy (ponieważ nie wiedzielibyśmy, kiedy to zrobić), więc zawsze istnieje odniesienie do niego i nigdy nie można go usunąć. W przykładzie WeakMap, gdy wszystkie inne odniesienia do obiektu znikną - obiekt można usunąć z WeakMap. Jeśli nadal nie jesteś pewien, co mam na myśli, daj mi znać
Benjamin Gruenbaum,
@Benjamin, Musimy rozróżnić między potrzebą pamięci podręcznej wrażliwej na pamięć a potrzebą krotki data_object. Nie łącz tych dwóch odrębnych wymagań. Twój calledprzykład jest lepiej napisany przy użyciu jsfiddle.net/f2efbm7z i nie pokazuje użycia słabej mapy. W rzeczywistości można go lepiej napisać na 6 sposobów, które wymienię poniżej.
Pacerier
Zasadniczo celem słabej mapy jest pamięć podręczna wrażliwa na pamięć. Chociaż może być używany do rozciągania przedmiotów z zewnątrz, jest to niepotrzebny kiepski hack i zdecydowanie nie jest jego właściwym celem .
Pacerier
1
Jeśli chcesz zachować połączenie między obietnicą a liczbą jej obsłużenia / odrzucenia, użyj 1) symbolu; p[key_symbol] = data. lub 2) unikalne nazewnictwo; p.__key = data. lub 3) zakres prywatny; (()=>{let data; p.Key = _=>data=_;})(). lub 4) zastępstwo z 1 lub 2 lub 3. lub 5) zastąpienie / rozszerzenie klasy Promise na 1 lub 2 lub 3. lub 6) zastąpienie / rozszerzenie klasy Promise z krotką potrzebnych członków. - W każdym razie słaba mapa nie jest potrzebna, chyba że potrzebujesz pamięci podręcznej wrażliwej na pamięć.
Pacerier
48

Ta odpowiedź wydaje się stronnicza i bezużyteczna w scenariuszu z prawdziwego świata. Przeczytaj ją w obecnej postaci i nie traktuj jej jako rzeczywistej opcji do niczego poza eksperymentowaniem

Przypadkiem użycia może być użycie go jako słownika dla słuchaczy, mam współpracownika, który to zrobił. Jest to bardzo pomocne, ponieważ każdy słuchacz jest bezpośrednio ukierunkowany na ten sposób działania. Pożegnanie listener.on.

Ale z bardziej abstrakcyjnego punktu widzenia, WeakMapjest szczególnie potężny do dematerializacji dostępu do praktycznie wszystkiego, nie potrzebujesz przestrzeni nazw, aby odizolować jej członków, ponieważ jest to już sugerowane przez naturę tej struktury. Jestem prawie pewien, że możesz poczynić pewne znaczące ulepszenia pamięci, zastępując niewygodne redundantne klucze obiektowe (nawet jeśli dekonstrukcja działa za Ciebie).


Przed przeczytaniem, co dalej

Teraz zdaję sobie sprawę, że moje podkreślenie nie jest najlepszym sposobem na rozwiązanie tego problemu i jak zauważył Benjamin Gruenbaum (sprawdź swoją odpowiedź, jeśli nie jest już powyżej mojej: p), problem ten nie mógł zostać rozwiązany regularnie Map, ponieważ wyciekłby, dlatego główną siłą WeakMapjest to, że nie przeszkadza w usuwaniu śmieci, ponieważ nie przechowują referencji.


Oto aktualny kod mojego współpracownika (dziękuję mu za udostępnienie)

Pełne źródło tutaj , dotyczy zarządzania słuchaczami, o którym mówiłem powyżej (możesz również zapoznać się ze specyfikacją )

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
źródło
2
Nie bardzo rozumiem, jak byś tego użył. Spowodowałoby to, że obserwowalne zawaliłoby się wraz ze związanymi z nim zdarzeniami, gdy nie były już przywoływane. Problem, który zwykle mam, polega na tym, że nie ma już odniesienia do obserwatora. Myślę, że rozwiązanie tutaj rozwiązało tylko połowę problemu. Nie sądzę, że można rozwiązać problem obserwatora za pomocą WeakMap, ponieważ nie można go iterować.
jgmjgm
1
Detektory zdarzeń z podwójnym buforowaniem mogą być szybkie w innych językach, ale w tym przypadku są po prostu ezoteryczne i powolne. To moje trzy centy.
Jack Giffin,
@axelduch, Wow, ten mit obsługi słuchacza został sprzedany aż do społeczności Javascript, zdobywając 40 głosów pozytywnych! Aby zrozumieć, dlaczego ta odpowiedź jest całkowicie błędna , zobacz komentarze pod stackoverflow.com/a/156618/632951
Pacerier
1
@Pacerier zaktualizował odpowiedź, dziękuję za opinię
axelduch
1
@axelduch, Tak, tam też jest referencja.
Pacerier
18

WeakMap działa dobrze do enkapsulacji i ukrywania informacji

WeakMapjest dostępna tylko dla ES6 i wyższych. A WeakMapto zbiór par kluczy i wartości, w których klucz musi być przedmiotem. W poniższym przykładzie tworzymy WeakMapz dwoma elementami:

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

Zastosowaliśmy tę set()metodę, aby zdefiniować powiązanie między obiektem a innym przedmiotem (w naszym przypadku ciąg znaków). Zastosowaliśmy tę get()metodę, aby pobrać element powiązany z obiektem. Ciekawym aspektem WeakMapjest to, że ma słabe odniesienie do klucza wewnątrz mapy. Słaba referencja oznacza, że ​​jeśli obiekt zostanie zniszczony, śmieciarz usunie cały wpis z WeakMap, zwalniając w ten sposób pamięć.

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;
    }());
})()
Michał Horojański
źródło
4
Re „słaba mapa działa dobrze w przypadku enkapsulacji i ukrywania informacji”. To, że możesz, nie oznacza, że ​​powinieneś. JavaScript ma domyślny sposób wykonywania enkapsulacji i ukrywania informacji nawet przed wynalezieniem słabej mapy. Na chwilę obecną istnieje dosłownie 6 sposobów . Użycie słabej mapy do wykonania enkapsulacji to brzydka twarz.
Pacerier
12

𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮

Słabych map można używać do przechowywania metadanych dotyczących elementów DOM bez ingerencji w wyrzucanie elementów bezużytecznych lub denerwowanie współpracowników na Twój kod. Na przykład możesz użyć ich do numerycznego indeksowania wszystkich elementów na stronie internetowej.

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

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

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

Różnica może wydawać się znikoma, poza tym, że wersja słabej mapy jest dłuższa, jednak istnieje zasadnicza różnica między dwoma fragmentami kodu pokazanymi powyżej. W pierwszym fragmencie kodu, bez słabych map, fragment kodu przechowuje odwołania we wszystkich kierunkach między elementami DOM. Zapobiega to gromadzeniu elementów DOM.(i * i) % lenmoże wydawać się dziwną kulką, której nikt by nie użył, ale pomyśl jeszcze raz: mnóstwo kodu produkcyjnego zawiera odwołania DOM, które odbijają się w całym dokumencie. Teraz, jeśli chodzi o drugi fragment kodu, ponieważ wszystkie odwołania do elementów są słabe, po usunięciu węzła przeglądarka jest w stanie stwierdzić, że węzeł nie jest używany (kod nie jest dostępny) w ten sposób usuń go z pamięci. Powodem, dla którego powinieneś martwić się wykorzystaniem pamięci i kotwicami pamięci (takimi jak pierwszy fragment kodu, w którym nieużywane elementy są przechowywane w pamięci) jest to, że większe użycie pamięci oznacza więcej prób GC w przeglądarce (w celu zwolnienia pamięci do zapobiegać awariom przeglądarki) oznacza wolniejsze przeglądanie, a czasem awarię przeglądarki.

Jeśli chodzi o polifill dla nich, poleciłbym własną bibliotekę ( tutaj @ github ). Jest to bardzo lekka biblioteka, która po prostu wypełni ją bez żadnych skomplikowanych ram, które można znaleźć w innych wypełniaczach.

~ Szczęśliwego kodowania!

Jack Giffin
źródło
1
Dzięki za jasne wyjaśnienie. Przykład jest wart więcej niż jakiekolwiek słowa.
newguy
@ Lolzery, Re „ Zapobiega to gromadzeniu elementów DOM ”, wszystko czego potrzebujesz to ustawić elementsna zero i gotowe: To będzie GCed. & Re „ Referencje DOM, które odbijają się w całym dokumencie ”, w ogóle nie mają znaczenia: gdy główny link elementszniknie, wszystkie okólniki będą oznaczone jako GCed. Jeśli twój element utrzymuje odwołania do elementu, którego nie potrzebuje, to popraw kod i ustaw ref na null, gdy skończysz z niego korzystać. To będzie GCed. Słabe mapy nie są potrzebne .
Pacerier
2
@Pacerier dziękuję za entuzjastyczne opinie, jednak ustawienie elementswartości null nie pozwoli przeglądarce na GC elementów w pierwszej sytuacji fragmentu kodu. Wynika to z faktu, że ustawiasz niestandardowe właściwości elementów, a następnie można uzyskać te elementy, a ich niestandardowe właściwości są nadal dostępne, zapobiegając w ten sposób GC'edowi. Pomyśl o tym jak o łańcuchu metalowych pierścieni. Oprócz tego masz dostęp do co najmniej jednego ogniwa w łańcuchu, możesz przytrzymać to ogniwo w łańcuchu, a tym samym zapobiec wpadnięciu całego łańcucha przedmiotów w otchłań.
Jack Giffin,
1
kod produkcyjny z dunder o nazwie vars sprawia, że ​​wymiotuję
Barbu Barbu
10

Używam WeakMapdo pamięci podręcznej bezproblemowego zapamiętywania funkcji, które przyjmują obiekty niezmienne jako ich parametr.

Zapamiętywanie to fantazyjny sposób powiedzenia „po obliczeniu wartości zapisz ją w pamięci podręcznej, abyś nie musiał jej ponownie obliczać”.

Oto przykład:

Kilka rzeczy do zapamiętania:

  • Obiekty Immutable.js zwracają nowe obiekty (z nowym wskaźnikiem) po ich modyfikacji, więc użycie ich jako kluczy w WeakMap gwarantuje taką samą obliczoną wartość.
  • WeakMap doskonale nadaje się do notatek, ponieważ gdy obiekt (użyty jako klucz) zostanie zrzucony śmieci, to także obliczona wartość WeakMap.
Rico Kahler
źródło
1
Jest to prawidłowe użycie słabej mapy, o ile pamięć podręczna zapamiętywania ma być wrażliwa na pamięć , a nie trwała przez cały czas obj / funkcji. Jeśli „pamięć podręczna zapamiętywania” ma być trwała przez całe życie obj / funkcji, słaba mapa jest złym wyborem: zamiast tego użyj dowolnej z 6 domyślnych technik enkapsulacji javascript .
Pacerier
3

Mam tę prostą przypadek użycia / Przykład dla WeakMaps.

ZARZĄDZAJ KOLEKCJĄ UŻYTKOWNIKÓW

Zacząłem z Userobiektu, którego właściwości należą fullname, username, age, genderoraz metodę zwaną printktóra drukuje czytelny dla człowieka podsumowanie innych właściwości.

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

Następnie dodałem Mapę, usersaby zachować zbiór wielu użytkowników, których kluczem jest username.

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

Dodanie kolekcji wymagało również funkcji pomocniczych do dodawania, pobierania, usuwania użytkownika, a nawet funkcji drukowania wszystkich użytkowników w celu zapewnienia kompletności.

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

Przy włączonym całym powyższym kodzie, powiedzmy NodeJS , tylko usersmapa ma odniesienie do obiektów użytkownika w całym procesie. Nie ma innych odniesień do poszczególnych obiektów użytkownika.

Uruchamiając ten kod interaktywną powłokę NodeJS, tak jak w przykładzie dodaję czterech użytkowników i drukuję ich: Dodawanie i drukowanie użytkowników

DODAJ WIĘCEJ INFORMACJI DLA UŻYTKOWNIKÓW BEZ MODYFIKOWANIA ISTNIEJĄCEGO KODU

Powiedzmy teraz, że wymagana jest nowa funkcja, w której łącza użytkowników SMP (Social Media Platform) muszą być śledzone wraz z obiektami użytkownika.

Kluczem tutaj jest również to, że ta funkcja musi zostać zaimplementowana przy minimalnej interwencji w istniejący kod.

Jest to możliwe dzięki WeakMaps w następujący sposób.

Dodaję trzy osobne WeakMapy dla Twittera, Facebooka, 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();

Dodano funkcję pomocnika, getSMPWeakMapaby zwrócić WeakMap powiązaną z podaną nazwą SMP.

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

Funkcja dodawania linku SMP użytkownika do danej SMP WeakMap.

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

Funkcja drukowania tylko użytkowników obecnych na danym 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)}`)
        }
    });
}

Możesz teraz dodawać łącza SMP dla użytkowników, również z możliwością, że każdy użytkownik będzie miał łącze na wielu SMP.

... kontynuując wcześniejszy przykład, dodałem łącza SMP do użytkowników, wiele łączy dla użytkowników Bill i Sarah, a następnie wydrukowałem łącza dla każdego SMP osobno: Dodawanie łączy SMP do użytkowników i ich wyświetlanie

Teraz powiedz, że użytkownik został usunięty z usersmapy, dzwoniąc deleteUser. To usuwa jedyne odniesienie do obiektu użytkownika. To z kolei usunie również łącze SMP z dowolnego / wszystkich SMP WeakMaps (Garbage Collection), ponieważ bez obiektu użytkownika nie ma możliwości uzyskania dostępu do żadnego z jego łączy SMP.

... kontynuując Przykład, usuwam użytkownika Billa, a następnie drukuję linki o SMP, z którymi był powiązany:

Usunięcie konta użytkownika z mapy powoduje również usunięcie łączy SMP

Nie ma wymogu żadnego dodatkowego kodu, aby osobno usunąć łącze SMP osobno, a istniejący kod, zanim ta funkcja i tak nie zostanie zmodyfikowana.

Jeśli istnieje inny sposób dodania tej funkcji z / bez WeakMaps, prosimy o komentarz.

elektrokrata
źródło
_____nice______
Aleks