Qual è la differenza tra ES6 Map e WeakMap?


93

Guardando questa e questa pagina MDN sembra che l'unica differenza tra Maps e WeakMaps sia una proprietà "size" mancante per WeakMaps. Ma è vero? Qual è la differenza tra loro?


L'effetto è sul GC. WeakMaps può far ritirare le chiavi.
John Dvorak

@JanDvorak non c'è nessun esempio puntato su MDN al riguardo. Come aWeakMap.get (chiave); // dire, 2 ... (azione GC) ... aWeakMap.get (chiave); // say, undefined
Dmitrii Sorin

1
Il tuo esempio è impossibile. keynon può essere raccolto, perché è referenziato da te.
John Dvorak

1
La decisione progettuale è che le azioni GC sono invisibili in Javascript. Non puoi osservare GC mentre fa le sue cose.
John Dvorak

1
Vedere questa risposta correlata per ulteriori informazioni su questo problema.
Benjamin Gruenbaum

Risposte:


53

Dalla stessa pagina, sezione " Why Weak Map? " :

Il programmatore JavaScript esperto noterà che questa API potrebbe essere implementata in JavaScript con due array (uno per le chiavi, uno per i valori) condivisi dai 4 metodi API. Una simile implementazione avrebbe due principali inconvenienti. Il primo è una ricerca O (n) (n è il numero di chiavi nella mappa). Il secondo è un problema di perdita di memoria. Con le mappe scritte manualmente, l'array di chiavi manterrebbe i riferimenti agli oggetti chiave, impedendo che vengano raccolti in modo indesiderato. Nelle WeakMap native, i riferimenti agli oggetti chiave vengono mantenuti "debolmente" , il che significa che non impediscono la raccolta dei rifiuti nel caso in cui non ci siano altri riferimenti all'oggetto.

Poiché i riferimenti sono deboli, le chiavi WeakMap non sono enumerabili (cioè non esiste un metodo che ti dia un elenco delle chiavi). Se lo fossero, l'elenco dipenderebbe dallo stato della garbage collection, introducendo il non determinismo.

[Ed è per questo che anche loro non hanno sizeproprietà]

Se vuoi avere un elenco di chiavi, dovresti mantenerlo da solo. C'è anche una proposta ECMAScript che mira a introdurre semplici insiemi e mappe che non utilizzerebbero riferimenti deboli e sarebbero enumerabili.

- che sarebbero i "normali" Maps . Non menzionato in MDN, ma nella proposta di armonia , quelli hanno anche items, keyse valuesmetodi di generatore e implementare l' Iteratorinterfaccia .


quindi new Map().get(x)ha circa lo stesso tempo di ricerca della lettura di una proprietà da un oggetto semplice?
Alexander Mills

1
@AlexanderMills Non vedo cosa abbia a che fare con la domanda, ma ecco alcuni dati . In generale, sì, sono simili e dovresti usare quello appropriato .
Bergi

Quindi la mia comprensione è che Map mantiene un array interno per mantenere la sua chiave a causa di quell'array. Il Garbage Collector non è in grado di trattenere il riferimento. In WeekMap, non ha un array in cui le chiavi vengono mantenute, quindi la chiave senza riferimento può essere raccolta in modo indesiderato.
Mohan Ram

@MohanRam A WeakMapha ancora un array (o un'altra raccolta) di voci, dice semplicemente al garbage collector che si tratta di riferimenti deboli .
Bergi

Allora perché l'iterazione per le chiavi WeekMap non è supportata?
Mohan Ram

92

Entrambi si comportano in modo diverso quando un oggetto a cui fanno riferimento le loro chiavi / valori viene eliminato. Prendiamo il seguente codice di esempio:

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

L'IIFE di cui sopra viene eseguito, non è possibile fare riferimento {x: 12}e non è {y: 12}più. Garbage Collector va avanti e cancella il puntatore chiave b da "WeakMap" e rimuove anche {y: 12}dalla memoria. Ma in caso di "Map", il garbage collector non rimuove un puntatore da "Map" e inoltre non rimuove {x: 12}dalla memoria.

Riepilogo: WeakMap consente a Garbage Collector di svolgere il proprio compito ma non Map.

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


12
Perché non viene rimosso dalla memoria? Perché puoi ancora farci riferimento! map.entries().next().value // [{x:12}, 1]
Bergi

4
Non è una funzione auto invocata. È un'espressione di funzione richiamata immediatamente. benalman.com/news/2010/11/…
Olson.dev

allora qual è la differenza tra weakmap e object
Muhammad Umer

@MuhammadUmer: l'oggetto può avere solo stringhe 'chiavi', mentre WeakMappuò avere solo chiavi non primitive (nessuna stringa o numero o Symbols come chiavi, solo array, oggetti, altre mappe, ecc.).
Ahmed Fasih

1
@nnnnnn Sì, questa è la differenza, è ancora in Map ma non inWeakMap
Alexander Derck

75

Forse la prossima spiegazione sarà più chiara per qualcuno.

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

Come vedi, dopo aver rimosso la k1chiave dalla memoria possiamo ancora accedervi all'interno della mappa. Allo stesso tempo, la rimozione della k2chiave di WeakMap la rimuove wmanche per riferimento.

Ecco perché WeakMap non ha metodi enumerabili come forEach, perché non esiste un elenco di chiavi WeakMap, sono solo riferimenti ad altri oggetti.


10
nell'ultima riga, ovviamente, wm.get (null) non sarà definito.
DaNeSh

8
Risposta migliore che copiare e incollare dal sito mozilla, complimenti.
Joel Hernandez

2
in forEach, (key, val)dovrebbe essere effettivamente(val, key)
Miguel Mota

incredibile come un esempio che non ha senso ottenga così tanti voti positivi
alfredopacino

34

Un'altra differenza (fonte: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Le chiavi di WeakMaps sono solo del tipo Object. I tipi di dati primitivi come chiavi non sono consentiti (ad esempio, un simbolo non può essere una chiave WeakMap).

Né è possibile utilizzare una stringa, un numero o un valore booleano come WeakMapchiave. A Map può usare valori primitivi per le chiavi.

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

m = new Map
m.set('a', 'b'); // Works

6
Nel caso qualcuno si chieda: posso immaginare la ragione dietro questo è: non puoi mantenere o passare riferimenti a tipi primitivi. Quindi la chiave in una WeakMap sarebbe il suo unico riferimento, in assoluto. In questo modo la raccolta dei rifiuti non sarebbe possibile. Non so se i riferimenti deboli siano impossibili o semplicemente non abbiano senso. Ma in entrambi i casi la chiave deve essere qualcosa che può essere referenziato debolmente.
Andreas Linnert

3

A partire dal Javascript.info

Mappa : se usiamo un oggetto come chiave in una mappa normale, finché esiste la mappa, esiste anche quell'oggetto. Occupa memoria e potrebbe non essere sottoposto a Garbage Collection.

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]

In modo simile, se usiamo un oggetto come chiave in una mappa normale, finché esiste la mappa, esiste anche quell'oggetto. Occupa memoria e potrebbe non essere sottoposto a Garbage Collection

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 - Ora, se usiamo un oggetto come chiave in esso e non ci sono altri riferimenti a quell'oggetto, verrà rimosso automaticamente dalla memoria (e dalla mappa).

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

// john is removed from memory!

3

WeapMap in javascript non contiene alcuna chiave o valore, manipola semplicemente il valore della chiave utilizzando un ID univoco e definisce una proprietà per l'oggetto chiave.

poiché definisce la proprietà in key objectbase al metodo Object.definePropert(), la chiave non deve essere di tipo primitivo .

e anche poiché WeapMap non contiene effettivamente coppie di valori chiave, non possiamo ottenere la proprietà length di weakmap.

e anche il valore manipolato viene assegnato all'oggetto chiave, il garbage collector può facilmente raccogliere la chiave se non viene utilizzata.

Codice di esempio per l'implementazione.

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

riferimento di attuazione


1
Per essere chiari, questa implementazione funziona solo a metà. Non consentirà di utilizzare lo stesso oggetto come chiave in più mappe deboli. Inoltre non funziona per oggetti congelati. E, naturalmente, fa trapelare la mappatura a chiunque abbia un riferimento all'oggetto. Il primo può essere risolto utilizzando simboli, ma non gli ultimi due.
Andreas Rossberg

@AndreasRossberg In questa implementazione ho aggiunto hardcoded id, ma questo dovrebbe essere unico usando qualcosa di Math.random e Date.now (), ecc. E aggiungendo questo id dinamico, il primo punto può essere risolto. Potrebbe fornirmi una soluzione per gli ultimi due punti.
Ravi Sevta

Il primo problema viene risolto in modo più elegante utilizzando i simboli. Gli ultimi due non possono essere risolti all'interno di JS, motivo per cui WeakMap deve essere un primitivo nella lingua.
Andreas Rossberg

1

WeakMap le chiavi devono essere oggetti, non valori primitivi.

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

Perché????

Vediamo sotto l'esempio.

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

Se usiamo un oggetto come chiave in un normale Map, allora mentre il Map esiste, esiste anche quell'oggetto. Occupa memoria e potrebbe non essere sottoposto a Garbage Collection.

WeakMapè fondamentalmente diverso in questo aspetto. Non impedisce la garbage collection di oggetti chiave.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

se usiamo un oggetto come chiave al suo interno e non ci sono altri riferimenti a quell'oggetto, verrà rimosso automaticamente dalla memoria (e dalla mappa).

WeakMap non supporta iterazioni e metodi keys () , values ​​() , entry () , quindi non c'è modo di ottenere tutte le chiavi oi valori da esso.

WeakMap ha solo i seguenti metodi:

  • weakMap.get (chiave)
  • weakMap.set (chiave, valore)
  • weakMap.delete (chiave)
  • weakMap.has (chiave)

È ovvio come se un oggetto avesse perso tutti gli altri riferimenti (come "utente" nel codice sopra), quindi deve essere raccolto automaticamente. Ma tecnicamente non è esattamente specificato quando avviene la pulizia.

Il motore JavaScript lo decide. Può scegliere di eseguire immediatamente la pulizia della memoria o di attendere ed eseguire la pulizia in un secondo momento quando si verificano più eliminazioni. Quindi, tecnicamente il conteggio degli elementi correnti di a WeakMapnon è noto. Il motore potrebbe averlo ripulito o meno o lo ha fatto parzialmente. Per questo motivo, i metodi che accedono a tutte le chiavi / valori non sono supportati.

Nota: - L'area principale di applicazione di WeakMap è una memoria dati aggiuntiva. Come la memorizzazione nella cache di un oggetto fino a quando tale oggetto non viene raccolto dalla spazzatura.

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.