Articles

WeakMaps (`WeakMap`) (advanced) – JavaScript pentru programatori nerăbdători (ediția ES2021)

(Ad, please don’t block.)

34 WeakMaps (WeakMap) (advanced)

  • 34.1 WeakMaps sunt cutii negre
  • 34.2 Cheile unui WeakMap sunt deținute slab
    • 34.2.1 Toate cheile WeakMap trebuie să fie obiecte
    • 34.2.2 Caz de utilizare: atașarea de valori la obiecte
  • 34.3 Exemple
    • 34.3.1 Stocarea în cache a rezultatelor calculate prin intermediul WeakMaps
    • 34.3.2 Păstrarea datelor private în WeakMaps
  • 34.4 API-ul WeakMap

WeakMaps sunt similare cu Maps, cu următoarele diferențe:

  • Sunt cutii negre, în care o valoare poate fi accesată numai dacă aveți atât WeakMap cât și cheia.
  • Celele unui WeakMap sunt deținute slab: dacă un obiect este o cheie într-un WeakMap, acesta poate fi în continuare colectat la gunoi. Acest lucru ne permite să folosim WeakMaps pentru a atașa date la obiecte.

Următoarele două secțiuni examinează mai detaliat ce înseamnă acest lucru.

34.1 WeakMaps sunt cutii negre

Este imposibil de inspectat ceea ce se află în interiorul unui WeakMap:

  • De exemplu, nu puteți itera sau face buclă peste chei, valori sau intrări. Și nu puteți calcula dimensiunea.
  • În plus, nu puteți nici să ștergeți un WeakMap – trebuie să creați o nouă instanță.

Aceste restricții activează o proprietate de securitate. Citându-l pe Mark Miller:

Corelarea de la valoarea perechii weakmap/cheie poate fi observată sau afectată doar de cineva care are atât weakmap cât și cheia. Cu clear(), cineva care ar fi avut doar WeakMap ar fi putut să afecteze maparea dintre WeakMap și perechea cheie-valoare.

34.2 Cheile unui WeakMap sunt slab deținute

Se spune că cheile unui WeakMap sunt slab deținute: În mod normal, dacă un obiect se referă la un alt obiect, atunci acesta din urmă nu poate fi colectat la gunoi atâta timp cât primul există. În cazul unui WeakMap, situația este diferită: dacă un obiect este o cheie și nu este menționat în altă parte, acesta poate fi colectat la gunoi atât timp cât WeakMap încă există. Acest lucru duce, de asemenea, la eliminarea intrării corespunzătoare (dar nu există nicio modalitate de a observa acest lucru).

34.2.1 Toate cheile WeakMap trebuie să fie obiecte

Toate cheile WeakMap trebuie să fie obiecte. Se obține o eroare dacă se folosește o valoare primitivă:

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

Cu valori primitive ca și chei, WeakMaps nu ar mai fi cutii negre. Dar, având în vedere că valorile primitive nu sunt niciodată colectate la gunoi, oricum nu profitați de cheile deținute slab și puteți la fel de bine să folosiți o Mapă normală.

34.2.2 Caz de utilizare: atașarea de valori la obiecte

Acesta este principalul caz de utilizare pentru WeakMaps: le puteți folosi pentru a atașa în exterior valori la obiecte – de exemplu:

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

În linia A, atașăm o valoare la obj. În linia B, obj poate fi deja colectat la gunoi, chiar dacă wm încă există. Această tehnică de atașare a unei valori la un obiect este echivalentă cu o proprietate a acelui obiect care este stocată în exterior. Dacă wm ar fi fost o proprietate, codul anterior ar fi arătat după cum urmează:

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

34.3 Exemple

34.3.1 Stocarea în memoria cache a rezultatelor calculate prin intermediul WeakMaps

Cu ajutorul WeakMaps, puteți asocia rezultatele calculate anterior cu obiecte fără a vă îngrijora de gestionarea memoriei. Următoarea funcție countOwnKeys() este un exemplu: aceasta stochează rezultatele anterioare în 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 ; }}

Dacă folosim această funcție cu un obiect obj, puteți vedea că rezultatul este calculat doar pentru prima apelare, în timp ce pentru a doua apelare se folosește o valoare stocată în cache:

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

34.3.2 Păstrarea datelor private în WeakMaps

În următorul cod, WeakMaps _counter și _action sunt folosite pentru a stoca valorile proprietăților virtuale ale instanțelor lui 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()), );

Acesta este modul în care este folosit Countdown:

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

Exercițiu: WeakMaps pentru date private

exercises/weakmaps/weakmaps_private_data_test.mjs

34.4 API WeakMap

Constructorul și cele patru metode din WeakMap funcționează la fel ca și echivalentele lor din Map:

  • new WeakMap<K, V>(entries?: Iterable<>) 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

Vezi aplicația quiz.

Următorul: 35 Seturi (Set)

.