W3docs

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 key

Mé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, ou undefined si 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));   // false

Pas 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]);   // undefined

Si 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));   // false

Son 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 / SetWeakMap / WeakSet
Clés / membresN'importe quelle valeurObjects uniquement
Force de la référenceForte (maintient les clés en vie)Faible (autorise le GC)
size, clear()OuiNon
Itération (forEach, keys…)OuiNon
Idéal pourCollections généralesDonné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.

Pratique

Pratique
Quelles sont les caractéristiques clés de WeakMap et WeakSet en JavaScript ?
Quelles sont les caractéristiques clés de WeakMap et WeakSet en JavaScript ?
Was this page helpful?