WeakMaps (`WeakMap`) (zaawansowane) – JavaScript dla niecierpliwych programistów (edycja ES2021)
34 WeakMaps (WeakMap) (zaawansowane)
- 34.1 WeakMapy to czarne skrzynki
- 34.2 Klucze WeakMap są słabo trzymane
- 34.2.1 Wszystkie klucze WeakMap muszą być obiektami
- 34.2.2 Przypadek użycia: dołączanie wartości do obiektów
- 34.3 Przykłady
- 34.3.1 Buforowanie obliczonych wyników za pomocą WeakMaps
- 34.3.2 Przechowywanie prywatnych danych w WeakMaps
- 34.4 API WeakMap
Mapy WeakMap są podobne do Map, z następującymi różnicami:
- Jest to czarna skrzynka, gdzie wartość może być dostępna tylko wtedy, gdy posiadasz zarówno WeakMap jak i klucz.
- Klucze WeakMap są słabo trzymane: jeśli obiekt jest kluczem w WeakMap, nadal może być zbierany śmieci. To pozwala nam używać WeakMap do dołączania danych do obiektów.
Następne dwie sekcje badają bardziej szczegółowo, co to oznacza.
34.1 WeakMapy są czarnymi skrzynkami
Nie można sprawdzić, co jest wewnątrz WeakMap:
- Na przykład, nie można iterować ani pętli nad kluczami, wartościami lub wpisami. I nie możesz obliczyć rozmiaru.
- Dodatkowo, nie możesz również wyczyścić WeakMap – musisz utworzyć świeżą instancję.
Te ograniczenia umożliwiają właściwość bezpieczeństwa. Cytując Mark Miller:
Odwzorowanie z wartości pary weakmap / key może być obserwowane lub dotknięte tylko przez kogoś, kto ma zarówno weakmap, jak i klucz. Z
clear()
, ktoś z tylko WeakMap byłby w stanie wpłynąć na mapowanie WeakMap-and-key-to-value.
34.2 The keys of a WeakMap are weakly held
O kluczach WeakMap mówi się, że są słabo utrzymywane: Normalnie, jeśli jeden obiekt odnosi się do innego, to ten drugi obiekt nie może być zaśmiecony, dopóki istnieje ten pierwszy. W przypadku WeakMap jest inaczej: Jeśli obiekt jest kluczem i nie ma do niego odniesienia w innym miejscu, może zostać wyrzucony na śmietnik, dopóki WeakMap nadal istnieje. To również prowadzi do usunięcia odpowiedniego wpisu (ale nie ma sposobu, aby to zaobserwować).
34.2.1 Wszystkie klucze WeakMap muszą być obiektami
Wszystkie klucze WeakMap muszą być obiektami. Otrzymasz błąd, jeśli użyjesz prymitywnej wartości:
> const wm = new WeakMap();> wm.set(123, 'test')TypeError: Invalid value used as weak map key
Z prymitywnymi wartościami jako kluczami, WeakMapy nie byłyby już czarnymi skrzynkami. Ale biorąc pod uwagę, że prymitywne wartości nigdy nie są zbierane do śmieci, i tak nie zyskujesz na słabo trzymanych kluczach i możesz równie dobrze użyć normalnej Mapy.
34.2.2 Przypadek użycia: dołączanie wartości do obiektów
To jest główny przypadek użycia WeakMaps: możesz ich użyć do zewnętrznego dołączania wartości do obiektów – na przykład:
const wm = new WeakMap();{ const obj = {}; wm.set(obj, 'attachedValue'); // (A)}// (B)
W linii A, dołączamy wartość do obj
. W linii B, obj
może już być zbierane do śmieci, mimo że wm
nadal istnieje. Ta technika dołączania wartości do obiektu jest równoważna z zewnętrznym przechowywaniem właściwości tego obiektu. Gdyby wm
był właściwością, poprzedni kod wyglądałby następująco:
{ const obj = {}; obj.wm = 'attachedValue';}
34.3 Przykłady
34.3.1 Buforowanie obliczonych wyników za pomocą WeakMaps
Dzięki WeakMaps można powiązać wcześniej obliczone wyniki z obiektami, nie martwiąc się o zarządzanie pamięcią. Następująca funkcja countOwnKeys()
jest przykładem: buforuje ona poprzednie wyniki w 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 ; }}
Jeśli użyjemy tej funkcji z obiektem obj
, można zauważyć, że wynik jest obliczany tylko dla pierwszego wywołania, podczas gdy zbuforowana wartość jest używana dla drugiego wywołania:
> const obj = { foo: 1, bar: 2};> countOwnKeys(obj)> countOwnKeys(obj)
34.3.2 Przechowywanie prywatnych danych w WeakMaps
W poniższym kodzie, WeakMaps _counter
i _action
są używane do przechowywania wartości wirtualnych właściwości instancji 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()), );
Tak jest używany 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);
Ćwiczenie: WeakMapy dla danych prywatnych
exercises/weakmaps/weakmaps_private_data_test.mjs
34.4 WeakMap API
Konstruktor i cztery metody WeakMap
działają tak samo jak ich odpowiedniki 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
Zobacz aplikację quizową.
Set
) .