Articles

WeakMaps (`WeakMap`) (advanced) – JavaScript pro netrpělivé programátory (vydání ES2021)

(Ad, please don’t block.)

34 WeakMaps (WeakMap) (advanced)

  • 34.1 WeakMaps jsou černé skříňky
  • 34.2 Klíče WeakMap jsou slabě drženy
    • 34.2.1 Všechny klíče WeakMap musí být objekty
    • 34.2.2 Případ použití: připojování hodnot k objektům
  • 34.3 Příklady
    • 34.3.1 Ukládání vypočtených výsledků do mezipaměti prostřednictvím WeakMaps
    • 34.3.2 Uchovávání soukromých dat v WeakMaps
  • 34.4 WeakMap API

WeakMaps jsou podobné Mapám s následujícími rozdíly:

  • Jsou to černé skříňky, kde k hodnotě lze přistupovat pouze v případě, že máte jak WeakMap, tak klíč
  • Klíče WeakMap jsou slabě drženy: pokud je objekt klíčem ve WeakMap, může být stále sbírán odpad. To nám umožňuje používat WeakMapy k připojování dat k objektům.

V následujících dvou částech podrobněji prozkoumáme, co to znamená.

34.1 WeakMapy jsou černé skříňky

Není možné kontrolovat, co je uvnitř WeakMap:

  • Například nelze iterovat nebo procházet klíče, hodnoty nebo položky. A nemůžete vypočítat velikost.
  • Kromě toho nemůžete WeakMap ani vymazat – musíte vytvořit novou instanci.

Tato omezení umožňují bezpečnostní vlastnost. Cituji Marka Millera:

Mapování z dvojice slabá mapa/hodnota klíče může pozorovat nebo ovlivnit pouze někdo, kdo má slabou mapu i klíč. S clear() by někdo, kdo má pouze slabou mapu, mohl ovlivnit mapování slabá mapa-a klíč-hodnota.

34.2 Klíče slabé mapy jsou slabě drženy

Říká se, že klíče slabé mapy jsou slabě drženy: Obvykle, pokud jeden objekt odkazuje na jiný objekt, pak tento druhý objekt nemůže být shromažďován jako odpad, dokud existuje ten první. U WeakMap je to jinak: Pokud je objekt klíčem a neodkazuje se na něj jinde, může být garbage-collected, dokud WeakMap existuje. To také vede k odstranění odpovídající položky (ale není způsob, jak to pozorovat).

34.2.1 Všechny klíče WeakMap musí být objekty

Všechny klíče WeakMap musí být objekty. Pokud použijete primitivní hodnotu, dostanete chybu:

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

Při použití primitivních hodnot jako klíčů by WeakMaps už nebyly černé skříňky. Ale vzhledem k tomu, že primitivní hodnoty se nikdy neshromažďují jako odpad, ze slabě držených klíčů stejně nic nemáte a můžete stejně dobře použít normální Mapu.

34.2.2 Případ použití: připojování hodnot k objektům

To je hlavní případ použití WeakMaps: můžete je použít k externímu připojování hodnot k objektům – například:

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

V řádku A připojujeme hodnotu k obj. V řádku B může být obj již odpadem, přestože wm stále existuje. Tato technika připojení hodnoty k objektu je ekvivalentní tomu, že vlastnost tohoto objektu je uložena externě. Kdyby wm byla vlastnost, vypadal by předchozí kód takto:

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

34.3 Příklady

34.3.1 Ukládání vypočtených výsledků do mezipaměti pomocí WeakMaps

Pomocí WeakMaps můžete k objektům přiřadit dříve vypočtené výsledky, aniž byste se museli starat o správu paměti. Příkladem je následující funkce countOwnKeys(): ukládá předchozí výsledky do mezipaměti 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 ; }}

Použijeme-li tuto funkci s objektem obj, vidíte, že výsledek se vypočítá pouze pro první volání, zatímco pro druhé volání se použije hodnota z mezipaměti:

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

34.3.2 Uchovávání soukromých dat v mapách WeakMaps

V následujícím kódu jsou mapy WeakMaps _counter a _action použity k uložení hodnot virtuálních vlastností instancí 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()), );

Takto se používá 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);

Cvičení: Slabé mapy pro soukromá data

exercises/weakmaps/weakmaps_private_data_test.mjs

34.4 WeakMap API

Konstruktor a čtyři metody WeakMap fungují stejně jako jejich ekvivalenty 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

Kvíz

Viz aplikace kvíz.

Další: 35 Sady (Set)