Quelle est la différence entre ES6 Map et WeakMap?


93

En regardant ceci et ces pages MDN, il semble que la seule différence entre Maps et WeakMaps est une propriété "size" manquante pour WeakMaps. Mais est-ce vrai? Quelle est la différence entre eux?


L'effet est sur le GC. WeakMaps peut récupérer ses clés.
John Dvorak

@JanDvorak, aucun exemple n'est indiqué sur MDN à ce sujet. Comme aWeakMap.get (clé); // dire, 2 ... (action GC) ... aWeakMap.get (clé); // dire, undefined
Dmitrii Sorin

1
Votre exemple est impossible. keyne peut pas être collecté, car il est référencé par vous.
John Dvorak

1
La décision de conception est que les actions GC sont invisibles en Javascript. Vous ne pouvez pas observer GC faire son truc.
John Dvorak

1
Consultez cette réponse connexe pour plus d'informations sur ce problème.
Benjamin Gruenbaum

Réponses:


53

De la même page, section " Pourquoi une carte faible ? " :

Le programmeur JavaScript expérimenté remarquera que cette API pourrait être implémentée en JavaScript avec deux tableaux (un pour les clés, un pour les valeurs) partagés par les 4 méthodes API. Une telle mise en œuvre présenterait deux inconvénients principaux. La première est une recherche O (n) (n étant le nombre de clés dans la carte). Le second est un problème de fuite de mémoire. Avec les cartes écrites manuellement, le tableau de clés conserverait les références aux objets clés, les empêchant d'être récupérés. Dans WeakMaps natif, les références aux objets clés sont conservées «faiblement» , ce qui signifie qu'elles n'empêchent pas le garbage collection au cas où il n'y aurait pas d'autre référence à l'objet.

En raison de la faiblesse des références, les clés WeakMap ne sont pas énumérables (c'est-à-dire qu'aucune méthode ne vous donne une liste des clés). Si tel était le cas, la liste dépendrait de l'état du garbage collection, introduisant le non-déterminisme.

[Et c'est pourquoi ils n'ont pas non plus de sizepropriété]

Si vous souhaitez avoir une liste de clés, vous devez la maintenir vous-même. Il existe également une proposition ECMAScript visant à introduire des ensembles et des cartes simples qui n'utiliseraient pas de références faibles et seraient énumérables.

- ce qui serait le "normal" Maps . Non mentionné chez MDN, mais dans la proposition d'harmonie , ceux-ci ont également items, keyset valuesgénérateurs de méthodes et implémentent l' Iteratorinterface .


a donc à new Map().get(x)peu près le même temps de recherche que la lecture d'une propriété à partir d'un objet ordinaire?
Alexander Mills

1
@AlexanderMills Je ne vois pas ce que cela a à voir avec la question, mais voici quelques données . En général, oui, ils sont similaires et vous devez utiliser celui qui convient .
Bergi

Donc, je crois comprendre que Map maintient un tableau interne pour conserver sa clé à cause de ce tableau. Le garbage collector n'est pas en mesure de retenir la référence. Dans WeekMap, il n'a pas de tableau où les clés sont conservées afin que les clés sans référence puissent être récupérées.
Mohan Ram

@MohanRam A a WeakMaptoujours un tableau (ou une autre collection) d'entrées, il indique simplement au garbage collector que ce sont des références faibles .
Bergi

Alors pourquoi l'itération pour les clés WeekMap n'est pas prise en charge?
Mohan Ram

92

Ils se comportent tous deux différemment lorsqu'un objet référencé par leurs clés / valeurs est supprimé. Prenons l'exemple de code ci-dessous:

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 ci-dessus est exécuté il n'y a aucun moyen de référencer {x: 12}et {y: 12}plus. Le garbage collector continue et supprime le pointeur clé b de «WeakMap» et supprime également {y: 12}de la mémoire. Mais dans le cas de «Map», le garbage collector ne supprime pas un pointeur de «Map» et ne le supprime pas non plus {x: 12}de la mémoire.

Résumé: WeakMap permet au garbage collector de faire sa tâche mais pas à Map.

Références: http://qnimate.com/difference-between-map-and-weakmap-in-javascript/


12
Pourquoi n'est-il pas supprimé de la mémoire? Parce que vous pouvez toujours le référencer! map.entries().next().value // [{x:12}, 1]
Bergi

4
Ce n'est pas une fonction auto-invoquée. C'est une expression de fonction immédiatement appelée. benalman.com/news/2010/11/…
Olson.dev

alors quelle est la différence entre lowmap et object
Muhammad Umer

@MuhammadUmer: l'objet ne peut avoir que des chaînes 'clés', alors que WeakMapne peut avoir que des clés non primitives (pas de chaînes ou de nombres ou de Symbols comme clés, uniquement des tableaux, des objets, d'autres cartes, etc.).
Ahmed Fasih

1
@nnnnnn Oui c'est la différence, c'est toujours dans le Map mais pas dans leWeakMap
Alexander Derck

75

Peut-être que la prochaine explication sera plus claire pour quelqu'un.

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

Comme vous le voyez, après avoir supprimé la k1clé de la mémoire, nous pouvons toujours y accéder à l'intérieur de la carte. En même temps, la suppression de la k2clé de WeakMap la supprime wmégalement par référence.

C'est pourquoi WeakMap n'a pas de méthodes énumérables comme forEach, car il n'existe pas de liste de clés WeakMap, ce ne sont que des références à un autre objet.


10
dans la dernière ligne, bien sûr, wm.get (null) sera indéfini.
DaNeSh

8
Meilleure réponse que copier et coller à partir du site mozilla, bravo.
Joel Hernandez

2
dans le forEach, (key, val)devrait être en fait(val, key)
Miguel Mota

incroyable comment un exemple qui n'a aucun sens obtient autant de votes positifs
alfredopacino

34

Autre différence (source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap ):

Les clés de WeakMaps sont du type Object uniquement. Les types de données primitifs en tant que clés ne sont pas autorisés (par exemple, un symbole ne peut pas être une clé WeakMap).

Une chaîne, un nombre ou un booléen ne peuvent pas non plus être utilisés comme WeakMapclé. A Map peut utiliser des valeurs primitives pour les clés.

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
Au cas où quelqu'un se demanderait: je peux imaginer que la raison derrière cela est: vous ne pouvez pas conserver ou transmettre des références aux types primitifs. Donc, la clé dans un WeakMap serait sa seule référence, jamais. De cette façon, le garbage collection ne serait pas possible. Je ne sais pas si les références faibles sont impossibles ou n'ont pas de sens. Mais de toute façon, la clé doit être quelque chose qui peut être référencé faiblement.
Andreas Linnert du

3

Depuis Javascript.info

Carte - Si nous utilisons un objet comme clé dans une carte régulière, alors que la carte existe, cet objet existe également. Il occupe de la mémoire et ne peut pas être récupéré.

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]

Similaire à cela, si nous utilisons un objet comme clé dans une carte normale, alors que la carte existe, cet objet existe également. Il occupe de la mémoire et peut ne pas être ramassé

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 - Maintenant, si nous utilisons un objet comme clé et qu'il n'y a pas d'autres références à cet objet - il sera automatiquement supprimé de la mémoire (et de la carte).

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

// john is removed from memory!

3

WeapMap en javascript ne contient aucune clé ou valeur, il manipule simplement la valeur de clé en utilisant un identifiant unique et définit une propriété pour l'objet clé.

car elle définit la propriété key objectpar méthode Object.definePropert(), la clé ne doit pas être de type primitif .

et aussi parce que WeapMap ne contient pas réellement de paires valeur / clé, nous ne pouvons pas obtenir la propriété length de lowmap.

et la valeur manipulée est également attribuée à l'objet clé, le garbage collector peut facilement collecter la clé si elle n'est pas utilisée.

Exemple de code pour l'implémentation.

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

référence de mise en œuvre


1
Pour être clair, cette implémentation ne fonctionne qu'à moitié. Il ne permettra pas d'utiliser le même objet comme clé dans plusieurs cartes faibles. Cela ne fonctionne pas non plus pour les objets gelés. Et bien sûr, il divulgue le mappage à quiconque a une référence à l'objet. Le premier peut être fixé à l'aide de symboles, mais pas les deux derniers.
Andreas Rossberg le

@AndreasRossberg Dans cette implémentation, j'ai ajouté un code en dur id, mais cela devrait être unique en utilisant quelque chose de Math.random et Date.now (), etc. Et en ajoutant cet identifiant dynamique, le premier point peut être résolu. Pouvez-vous me fournir une solution pour les deux derniers points.
Ravi Sevta

Le premier problème est résolu plus élégamment en utilisant des symboles. Les deux derniers ne peuvent pas être résolus dans JS, c'est pourquoi WeakMap doit être une primitive dans le langage.
Andreas Rossberg le

1

WeakMap les clés doivent être des objets et non des valeurs primitives.

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

Pourquoi????

Voyons l'exemple ci-dessous.

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

Si nous utilisons un objet comme clé dans un régulier Map, alors tant que le Mapexiste, cet objet existe également. Il occupe de la mémoire et ne peut pas être récupéré.

WeakMapest fondamentalement différent à cet égard. Cela n'empêche pas le garbage collection d'objets clés.

let user = { name: "User" };

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

user = null; // overwrite the reference

// 'user' is removed from memory!

si nous utilisons un objet comme clé, et qu'il n'y a pas d'autres références à cet objet - il sera automatiquement supprimé de la mémoire (et de la carte).

WeakMap ne prend pas en charge l'itération et les méthodes keys () , values ​​() , entries () , il n'y a donc aucun moyen d'en obtenir toutes les clés ou valeurs.

WeakMap n'a que les méthodes suivantes:

  • lowMap.get (clé)
  • lowMap.set (clé, valeur)
  • lowMap.delete (clé)
  • lowMap.has (clé)

C'est évident, comme si un objet avait perdu toutes les autres références (comme «utilisateur» dans le code ci-dessus), alors il doit être récupéré automatiquement. Mais techniquement, ce n'est pas exactement spécifié quand le nettoyage a lieu.

Le moteur JavaScript décide cela. Il peut choisir d'effectuer le nettoyage de la mémoire immédiatement ou d'attendre et de le faire plus tard lorsque d'autres suppressions se produisent. Donc, techniquement, le nombre d'éléments actuel de a WeakMapn'est pas connu. Le moteur peut l'avoir nettoyé ou non ou l'a fait partiellement. Pour cette raison, les méthodes qui accèdent à toutes les clés / valeurs ne sont pas prises en charge.

Remarque: - Le principal domaine d'application de WeakMap est un stockage de données supplémentaire. Comme mettre en cache un objet jusqu'à ce que cet objet soit récupéré.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.