WeakMaps (`WeakMap`) (advanced) – JavaScript pro netrpělivé programátory (vydání ES2021)
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.
Set
)