Articles

WeakMaps (`WeakMap`) (avancé) – JavaScript pour les programmeurs impatients (édition ES2021)

(Annonce, s’il vous plaît ne pas bloquer.)

34 WeakMaps (WeakMap) (avancé)

  • 34.1 Les WeakMaps sont des boîtes noires
  • 34.2 Les clés d’une WeakMap sont faiblement tenues
    • 34.2.1 Toutes les clés de WeakMap doivent être des objets
    • 34.2.2 Cas d’utilisation : attacher des valeurs aux objets
  • 34.3 Exemples
    • 34.3.1 Mise en cache des résultats calculés via les WeakMaps
    • 34.3.2 Conservation des données privées dans les WeakMaps
  • 34.4 API WeakMap

Les WeakMaps sont similaires aux Maps, avec les différences suivantes :

  • Ce sont des boîtes noires, où l’on ne peut accéder à une valeur que si l’on a à la fois la WeakMap et la clé.
  • Les clés d’une WeakMap sont faiblement tenues : si un objet est une clé dans une WeakMap, il peut toujours être mis à la poubelle. Cela nous permet d’utiliser les WeakMaps pour attacher des données aux objets.

Les deux sections suivantes examinent plus en détail ce que cela signifie.

34.1 Les WeakMaps sont des boîtes noires

Il est impossible d’inspecter ce qui se trouve à l’intérieur d’un WeakMap:

  • Par exemple, vous ne pouvez pas itérer ou boucler sur les clés, les valeurs ou les entrées. Et vous ne pouvez pas calculer la taille.
  • En outre, vous ne pouvez pas non plus effacer une WeakMap – vous devez créer une nouvelle instance.

Ces restrictions activent une propriété de sécurité. Citant Mark Miller:

Le mappage de la valeur de la paire weakmap/clé ne peut être observé ou affecté que par quelqu’un qui a à la fois le weakmap et la clé. Avec clear(), quelqu’un possédant uniquement la carte faible aurait pu affecter le mappage de la carte faible et de la clé vers la valeur.

34.2 Les clés d’une carte faible sont faiblement tenues

Les clés d’une carte faible sont dites faiblement tenues : Normalement, si un objet fait référence à un autre, alors ce dernier objet ne peut pas être mis à la poubelle tant que le premier existe. Avec une WeakMap, c’est différent : si un objet est une clé et n’est pas référencé ailleurs, il peut être mis à la poubelle tant que la WeakMap existe. Cela conduit également à la suppression de l’entrée correspondante (mais il n’y a aucun moyen de l’observer).

34.2.1 Toutes les clés de WeakMap doivent être des objets

Toutes les clés de WeakMap doivent être des objets. Vous obtenez une erreur si vous utilisez une valeur primitive:

> const wm = new WeakMap();> wm.set(123, 'test')TypeError: Invalid value used as weak map key

Avec des valeurs primitives comme clés, les WeakMaps ne seraient plus des boîtes noires. Mais étant donné que les valeurs primitives ne sont jamais ramassées à la poubelle, vous ne profitez pas des clés faiblement tenues de toute façon, et vous pouvez tout aussi bien utiliser une Map normale.

34.2.2 Cas d’utilisation : attacher des valeurs aux objets

C’est le principal cas d’utilisation des WeakMaps : vous pouvez les utiliser pour attacher extérieurement des valeurs aux objets – par exemple :

const wm = new WeakMap();{ const obj = {}; wm.set(obj, 'attachedValue'); // (A)}// (B)

Dans la ligne A, nous attachons une valeur à obj. À la ligne B, obj peut déjà être mis à la poubelle, même si wm existe toujours. Cette technique consistant à attacher une valeur à un objet est équivalente à une propriété de cet objet stockée en externe. Si wm était une propriété, le code précédent ressemblerait à ceci:

{ const obj = {}; obj.wm = 'attachedValue';}

34.3 Exemples

34.3.1 Mise en cache des résultats calculés via WeakMaps

Avec WeakMaps, vous pouvez associer des résultats précédemment calculés à des objets sans avoir à vous soucier de la gestion de la mémoire. La fonction suivante countOwnKeys() est un exemple : elle met en cache les résultats précédents dans la WeakMap cache.

const cache = new WeakMap();function countOwnKeys(obj) { if (cache.has(obj)) { return ; } else { const count = Object.keys(obj).length; cache.set(obj, count); return ; }}

Si nous utilisons cette fonction avec un objet obj, vous pouvez voir que le résultat n’est calculé que pour la première invocation, tandis qu’une valeur mise en cache est utilisée pour la deuxième invocation :

> const obj = { foo: 1, bar: 2};> countOwnKeys(obj)> countOwnKeys(obj)

34.3.2 Conserver les données privées dans les WeakMaps

Dans le code suivant, les WeakMaps _counter et _action sont utilisées pour stocker les valeurs des propriétés virtuelles des instances de Countdown:

const _counter = new WeakMap();const _action = new WeakMap();class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } }}// The two pseudo-properties are truly private:assert.deepEqual( Object.keys(new Countdown()), );

Voici comment Countdown est utilisé:

let invoked = false;const cd = new Countdown(3, () => invoked = true);cd.dec(); assert.equal(invoked, false);cd.dec(); assert.equal(invoked, false);cd.dec(); assert.equal(invoked, true);

Exercice : WeakMaps pour les données privées

exercises/weakmaps/weakmaps_private_data_test.mjs

34.4 API WeakMap

Le constructeur et les quatre méthodes de WeakMap fonctionnent de la même manière que leurs équivalents Map :

  • new WeakMap<K, V>(entries?: Iterable<>)
  • .delete(key: K) : boolean
  • .get(key: K) : V
  • .has(key: K) : boolean
  • .set(key: K, value: V) : this

Quiz

Voir l’application quiz.

Suivant : 35 Sets (Set)