JavaScript WeakMap et WeakSet
Dans ce chapitre, nous vous fournissons des informations complètes sur WeakMap et WeakSet, deux moyens utiles de stocker des données en JavaScript.
Introduction à JavaScript WeakMap et WeakSet
WeakMap et WeakSet sont des versions spécialisées de Map et Set. Leur utilisation est presque identique, mais ils diffèrent sur un point décisif : ils maintiennent leurs clés (ou membres) de manière faible. Une référence faible n'empêche pas le moteur JavaScript de supprimer un object de la mémoire. Si la seule chose qui pointe vers un object est une clé de WeakMap ou un membre de WeakSet, l'object peut toujours être collecté par le ramasse-miettes.
Cette propriété donne à WeakMap et WeakSet leur raison d'être : ils vous permettent d'associer des données supplémentaires à un object sans contrôler la durée de vie de cet object. Lorsque l'object disparaît, les données associées disparaissent avec lui — automatiquement, sans code de nettoyage.
Ce chapitre couvre les règles qui les distinguent, les compromis que ces règles imposent, et les situations concrètes où ils constituent le bon outil.
Ce que signifie « faible »
Dans une Map classique, une clé est une référence forte. Tant que la Map existe, chaque clé qu'elle contient est maintenue en vie — même si aucune autre partie de votre programme n'utilise encore cette clé. C'est une source courante de fuites mémoire : vous oubliez de supprimer une entrée avec delete, et l'object clé persiste indéfiniment.
Un WeakMap inverse ce comportement. La clé est référencée faiblement, ce qui signifie que le moteur est libre de récupérer l'object clé dès que rien d'autre ne le référence. Quand cela se produit, l'entrée disparaît simplement du WeakMap.
let visits = new WeakMap();
let user = { name: "Alice" };
visits.set(user, 10);
console.log(visits.get(user)); // 10
console.log(visits.has(user)); // true
user = null; // the object is now unreachable elsewhere
// The WeakMap entry becomes eligible for garbage collection.
// You cannot observe exactly when it is removed.WeakMap
Un WeakMap est une collection de paires clé/valeur où les clés doivent être des objects et les valeurs peuvent être de n'importe quel type.
Les clés doivent être des objects
Les valeurs primitives (string, nombres, boolean, symbol, null, undefined) ne peuvent pas être des clés. La raison découle de la conception : les primitives ne sont pas collectées par le ramasse-miettes de la même façon que les objects, donc les « maintenir faiblement » n'a aucun sens. Tenter de l'utiliser lève une TypeError.
let wm = new WeakMap();
wm.set("a string", 1); // TypeError: Invalid value used as weak map keyMéthodes disponibles
Un WeakMap ne prend en charge que quatre opérations :
set(key, value)— stocker une valeur sous une clé object.get(key)— lire la valeur, ouundefinedsi absente.has(key)— vérifier si une clé existe.delete(key)— supprimer une entrée.
let wm = new WeakMap();
let key = { id: 1 };
wm.set(key, "data");
console.log(wm.has(key)); // true
console.log(wm.get(key)); // "data"
wm.delete(key);
console.log(wm.has(key)); // falsePas d'itération ni de taille
Il n'y a pas de propriété size, pas de clear(), et aucun moyen de boucler sur un WeakMap (pas de keys(), values(), entries(), ni forEach). Ce n'est pas un oubli. Parce que les entrées peuvent disparaître à tout moment lors de l'exécution du ramasse-miettes, le contenu est non-déterministe — les exposer permettrait à votre code d'observer le moment d'exécution du GC, ce que le langage cache délibérément.
let wm = new WeakMap();
console.log("size" in wm); // false
console.log(wm[Symbol.iterator]); // undefinedSi vous avez besoin de compter les entrées, d'itérer, ou de stocker des clés primitives, utilisez plutôt une Map classique.
WeakSet
Un WeakSet est l'équivalent de Set : une collection d'objects uniques maintenus faiblement. Comme WeakMap, ses membres doivent être des objects, et il n'offre ni itération ni size.
let visited = new WeakSet();
let a = { id: 1 };
let b = { id: 2 };
visited.add(a);
console.log(visited.has(a)); // true
console.log(visited.has(b)); // false
visited.delete(a);
console.log(visited.has(a)); // falseSon API complète se résume à add(value), has(value) et delete(value).
Cas d'utilisation pratiques
Mise en cache et mémoïsation
Mettez en cache le résultat d'un calcul coûteux indexé par l'object auquel il se rapporte. Étant donné que la clé est faible, le cache ne maintient jamais en vie un object qui n'est plus nécessaire.
const cache = new WeakMap();
function process(obj) {
if (cache.has(obj)) {
return cache.get(obj); // reuse the cached result
}
const result = obj.value * 2; // pretend this is expensive
cache.set(obj, result);
return result;
}
let data = { value: 21 };
console.log(process(data)); // 42 (computed)
console.log(process(data)); // 42 (from cache)Données privées par object
Stockez des données appartenant à un object sans ajouter de propriété à l'object lui-même (que n'importe qui pourrait lire ou écraser). C'est une façon classique d'émuler de véritables champs privés, liée à la manière dont les méthodes se lient aux objects via this.
const balances = new WeakMap();
class Account {
constructor(amount) {
balances.set(this, amount); // private to this module
}
deposit(n) {
balances.set(this, balances.get(this) + n);
}
get balance() {
return balances.get(this);
}
}
const acc = new Account(100);
acc.deposit(50);
console.log(acc.balance); // 150
// There is no `amount` property on `acc` itself to tamper with.Quand l'instance d'Account n'est plus référencée, son entrée de solde est collectée automatiquement.
Métadonnées pour les nœuds DOM
Une fuite fréquente dans les pages à longue durée de vie est de conserver une Map de métadonnées de nœuds DOM après que les nœuds ont été supprimés du document. Avec un WeakSet/WeakMap, une fois qu'un nœud est détaché et déréférencé, ses métadonnées sont également récupérées.
const seen = new WeakSet();
function markVisited(node) {
if (seen.has(node)) return false; // already processed
seen.add(node);
return true;
}
// When the node leaves the DOM and nothing else holds it,
// it disappears from `seen` without manual cleanup.WeakMap/WeakSet vs Map/Set
| Capacité | Map / Set | WeakMap / WeakSet |
|---|---|---|
| Clés / membres | N'importe quelle valeur | Objects uniquement |
| Force de la référence | Forte (maintient les clés en vie) | Faible (autorise le GC) |
size, clear() | Oui | Non |
Itération (forEach, keys…) | Oui | Non |
| Idéal pour | Collections générales | Données associées aux objects avec nettoyage automatique |
Choisissez la variante faible uniquement lorsque vous voulez spécifiquement que le moteur gère le nettoyage pour vous et que vous n'avez pas besoin d'énumérer le contenu. Pour tout le reste, utilisez la Map et Set classiques.