Jaka jest różnica między mapą ES6 a WeakMap?

94

Patrząc na i tę stronę MDN, wydaje się, że jedyną różnicą między Mapami a WeakMaps jest brakująca właściwość „size” dla WeakMaps. Ale czy to prawda? Jaka jest między nimi różnica?

Dmitrii Sorin
źródło
Efekt jest na GC. WeakMaps może mieć zebrane klucze.
John Dvorak,
@JanDvorak nie ma na to żadnego przykładu wskazanego w MDN. Podobnie jak aWeakMap.get (klucz); // powiedz, 2 ... (akcja GC) ... aWeakMap.get (klucz); // powiedz, undefined
Dmitrii Sorin
1
Twój przykład jest niemożliwy. keynie można zebrać, ponieważ odwołujesz się do niego.
John Dvorak,
1
Decyzja projektowa jest taka, że ​​akcje GC są niewidoczne w JavaScript. Nie możesz obserwować, jak GC robi swoje.
John Dvorak,
1
Zobacz tę powiązaną odpowiedź, aby uzyskać więcej informacji na temat tego problemu.
Benjamin Gruenbaum

Odpowiedzi:

54

Na tej samej stronie w sekcji „ Dlaczego słaba mapa? :

Doświadczony programista JavaScript zauważy, że ten interfejs API można zaimplementować w JavaScript z dwiema tablicami (jedną dla kluczy, jedną dla wartości) współdzielonymi przez 4 metody API. Taka implementacja miałaby dwie główne niedogodności. Pierwszym jest wyszukiwanie O (n) (n to liczba kluczy na mapie). Drugi to problem z wyciekiem pamięci. W przypadku map napisanych ręcznie tablica kluczy zachowałaby odniesienia do kluczowych obiektów, zapobiegając ich usuwaniu jako śmieci. W natywnych WeakMaps odwołania do kluczowych obiektów są utrzymywane „słabo” , co oznacza, że ​​nie zapobiegają one usuwaniu elementów bezużytecznych w przypadku, gdyby nie było innego odniesienia do obiektu.

Ponieważ odwołania są słabe, klucze WeakMap nie są wyliczalne (tj. Nie ma metody dającej listę kluczy). Gdyby tak było, lista zależałaby od stanu czyszczenia pamięci, wprowadzając niedeterminizm.

[I dlatego też nie mają sizewłasności]

Jeśli chcesz mieć listę kluczy, powinieneś ją utrzymywać samodzielnie. Istnieje również propozycja ECMAScript mająca na celu wprowadzenie prostych zestawów i map, które nie wykorzystywałyby słabych odniesień i byłyby wyliczalne.

- czyli „normalne” Maps . Niewymienionych w MDN, ale na wniosek harmonii , ci mają również items, keysi valuesmetody generatora oraz wdrożenie Iteratorinterfejsu .

Bergi
źródło
Czy więc czas wyszukiwania new Map().get(x)jest mniej więcej taki sam, jak odczyt właściwości ze zwykłego obiektu?
Alexander Mills
1
@AlexanderMills Nie wiem, co to ma wspólnego z pytaniem, ale oto kilka danych . Ogólnie tak, są podobne i powinieneś użyć odpowiedniego .
Bergi
Więc rozumiem, że Map utrzymuje wewnętrzną tablicę, aby zachować swój klucz z powodu tej tablicy. Odśmiecacz nie jest w stanie powstrzymać odniesienia. W WeekMap nie ma tablicy, w której utrzymywane są klucze, więc klucz bez odniesienia może zostać wyrzucony do pamięci.
Mohan Ram
@MohanRam A WeakMapnadal ma tablicę (lub inną kolekcję) wpisów, po prostu informuje moduł odśmiecania pamięci, że są to słabe odwołania .
Bergi
Dlaczego więc iteracja kluczy mapy tygodnia nie jest obsługiwana?
Mohan Ram
94

Oba zachowują się inaczej, gdy obiekt, do którego odwołują się ich klucze / wartości, zostanie usunięty. Weźmy poniższy przykładowy kod:

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

Powyższy IIFE jest wykonywany, nie ma możliwości odniesienia się do niego {x: 12}i {y: 12}już. Moduł wyrzucania elementów bezużytecznych działa dalej i usuwa wskaźnik klawisza b z „WeakMap”, a także usuwa {y: 12}z pamięci. Ale w przypadku „Mapy”, garbage collector nie usuwa wskaźnika z „Map”, a także nie usuwa {x: 12}z pamięci.

Podsumowanie: WeakMap pozwala garbage collectorowi wykonywać swoje zadanie, ale nie Map.

Źródła : http://qnimate.com/difference-between-map-and-weakmap-in-javascript/

kshirish
źródło
14
Dlaczego nie jest usuwany z pamięci? Ponieważ nadal możesz się do niego odwoływać! map.entries().next().value // [{x:12}, 1]
Bergi
4
Nie jest to funkcja wywoływana samodzielnie. Jest to natychmiast wywołane wyrażenie funkcyjne. benalman.com/news/2010/11/…
Olson.dev
w takim razie jaka jest różnica między słabą mapą a obiektem
Muhammad Umer
@MuhammadUmer: obiekt może mieć tylko ciąg „klucze”, podczas gdy WeakMapmoże mieć tylko nieprymitywne klucze (bez łańcuchów, liczb lub Symbols jako kluczy, tylko tablice, obiekty, inne mapy itp.).
Ahmed Fasih
1
@nnnnnn Tak, to jest różnica, nadal jest w, Map ale nie wWeakMap
Alexander Derck
77

Może następne wyjaśnienie będzie dla kogoś bardziej jasne.

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

Jak widzisz, po wyjęciu k1klucza z pamięci nadal mamy do niego dostęp wewnątrz mapy. W tym samym czasie usunięcie k2klucza WeakMap usuwa go wmrównież przez odniesienie.

Dlatego WeakMap nie ma wyliczalnych metod, takich jak forEach, ponieważ nie ma czegoś takiego jak lista kluczy WeakMap, są one po prostu odniesieniami do innych obiektów.

Rax Wunter
źródło
11
w ostatnim wierszu oczywiście wm.get (null) będzie niezdefiniowane.
DaNeSh
9
Lepsza odpowiedź niż kopiowanie i wklejanie ze strony Mozilli, kudos.
Joel Hernandez
2
w forEach, (key, val)należy być w rzeczywistości(val, key)
Miguel Mota
niewiarygodne, jak za przykład, który nie ma sensu, jest tak wiele głosów poparcia
alfredopacino
34

Kolejna różnica (źródło: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Klucze WeakMaps są tylko typu Object. Prymitywne typy danych jako klucze są niedozwolone (np. Symbol nie może być kluczem WeakMap).

Jako WeakMapklucza nie można też użyć ciągu, liczby ani wartości logicznej . A Map może używać wartości pierwotnych dla kluczy.

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
źródło
6
Na wypadek gdyby ktoś się zastanawiał: mogę sobie wyobrazić powód takiego stanu rzeczy: nie można zachować ani przekazać referencji do typów pierwotnych. Zatem klucz w WeakMap byłby jego jedynym odniesieniem, kiedykolwiek. W ten sposób nie byłoby możliwe zbieranie śmieci. Nie wiem, czy słabe odniesienia są niemożliwe, czy po prostu nie mają sensu. Ale tak czy inaczej klucz musi być czymś, do czego można się słabo odnosić.
Andreas Linnert
3

Z Javascript.info

Mapa - jeśli używamy obiektu jako klucza w zwykłej mapie, to gdy mapa istnieje, ten obiekt również istnieje. Zajmuje pamięć i nie może być usuwany jako śmieci.

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]

Podobnie, jeśli używamy obiektu jako klucza w zwykłej Mapie, to gdy Mapa istnieje, ten obiekt również istnieje. Zajmuje pamięć i nie może być usuwany jako śmieci

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 - Teraz, jeśli użyjemy w nim obiektu jako klucza i nie ma innych odniesień do tego obiektu - zostanie on automatycznie usunięty z pamięci (i mapy).

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

// john is removed from memory!
Avadhut Thorat
źródło
3

WeapMap w javascript nie przechowuje żadnych kluczy ani wartości, po prostu manipuluje wartością klucza przy użyciu unikalnego identyfikatora i definiuje właściwość obiektu klucza.

ponieważ definiuje właściwość key objectwedług metody Object.definePropert(), klucz nie może być typem pierwotnym .

a także ponieważ WeapMap nie zawiera w rzeczywistości par klucz-wartość, nie możemy uzyskać właściwości length ze słabe mapy.

a także manipulowana wartość jest przypisywana z powrotem do obiektu klucza, moduł odśmiecania pamięci może łatwo zebrać klucz, jeśli nie jest używany.

Przykładowy kod do implementacji.

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

odniesienie do realizacji

Ravi Sevta
źródło
1
Żeby było jasne, ta implementacja działa tylko w połowie. Nie pozwoli na użycie tego samego obiektu jako klucza w wielu słabych mapach. Nie działa również w przypadku zamrożonych obiektów. I oczywiście wycieka mapowanie do każdego, kto ma odniesienie do obiektu. Pierwszą można naprawić za pomocą symboli, ale dwie ostatnie nie.
Andreas Rossberg
@AndreasRossberg W tej implementacji dodałem zakodowane na stałe id, ale powinno to być unikalne przez użycie czegoś Math.random i Date.now (), itp. I dodając ten dynamiczny identyfikator, można rozwiązać pierwszy punkt. Czy mógłbyś podać mi rozwiązanie dwóch ostatnich punktów.
Ravi Sevta
Pierwszy problem rozwiązano bardziej elegancko za pomocą symboli. Te dwa ostatnie nie mogą być rozwiązane w JS, dlatego WeakMap musi być prymitywem w języku.
Andreas Rossberg
1

WeakMap klucze muszą być obiektami, a nie wartościami pierwotnymi.

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

Czemu????

Zobaczmy poniższy przykład.

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

Jeśli użyjemy obiektu jako klucza w zwykłym Map, to dopóki Mapistnieje, ten obiekt również istnieje. Zajmuje pamięć i nie może być usuwany jako śmieci.

WeakMapzasadniczo różni się pod tym względem. Nie zapobiega usuwaniu śmieci z kluczowych obiektów.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

jeśli użyjemy w nim obiektu jako klucza i nie ma innych odniesień do tego obiektu - zostanie on automatycznie usunięty z pamięci (i mapy).

WeakMap nie obsługuje iteracji i metod keys () , values ​​() , entry () , więc nie ma możliwości uzyskania z nich wszystkich kluczy lub wartości.

WeakMap ma tylko następujące metody:

  • słabyMap.get (klucz)
  • słabyMap.set (klucz, wartość)
  • słabyMap.delete (klucz)
  • slabyMap.has (klucz)

Jest to oczywiste, tak jakby obiekt stracił wszystkie inne odniesienia (jak „user” w powyższym kodzie), to ma zostać automatycznie zebrany jako śmieci. Ale technicznie nie jest dokładnie określone, kiedy nastąpi czyszczenie.

Decyduje o tym silnik JavaScript. Może zdecydować się na natychmiastowe wyczyszczenie pamięci lub zaczekać i wykonać czyszczenie później, gdy nastąpi więcej usunięć. Tak więc technicznie aktualna liczba elementów a WeakMapnie jest znana. Silnik mógł go wyczyścić lub nie, lub zrobił to częściowo. Z tego powodu metody uzyskujące dostęp do wszystkich kluczy / wartości nie są obsługiwane.

Uwaga: - Głównym obszarem zastosowania WeakMap jest dodatkowe przechowywanie danych. Podobnie jak w przypadku buforowania obiektu, dopóki ten obiekt nie zostanie usunięty.

Pravin Divraniya
źródło