Kolekcje: WeakSet i WeakMap
Zadanie 5.2: Kolekcje Słabe (Weak Collections)
Wstęp
JavaScript automatycznie zarządza pamięcią (Garbage Collection). Kiedy coś nie jest już potrzebne, jest usuwane z pamięci. Jednak "zwykłe" Set i Map trzymają "silne" referencje do swoich elementów – dopóki element jest w kolekcji, nie zostanie usunięty z pamięci, nawet jeśli nigdzie indziej w programie nie jest używany.
WeakSet i WeakMap trzymają "słabe" referencje. Jeśli obiekt istnieje TYLKO w WeakMap, Garbage Collector może go usunąć.
Cel zadania
Zrozumienie różnic między silnymi a słabymi referencjami oraz zastosowanie WeakMap do przechowywania prywatnych danych obiektów.
Wymagania techniczne
- Stworzenie
WeakMap. - Zrozumienie ograniczeń (kluczem musi być obiekt, brak metod iteracyjnych).
- Symulacja usuwania obiektu z pamięci (teoretyczna).
Kroki do wykonania
1. WeakMap vs Map
Główne różnice:
- Kluczem w
WeakMap(i elementem wWeakSet) musi być obiekt. Nie może to być liczba ani napis. WeakMapnie jest iterowalna (brakforEach,size,entries).
Stwórz plik weak.js:
let user = { name: "Jan" };
const strongMap = new Map();
const weakMap = new WeakMap();
strongMap.set(user, "Dane Jana");
weakMap.set(user, "Dane Jana (Weak)");
console.log(strongMap.get(user)); // "Dane Jana"
console.log(weakMap.get(user)); // "Dane Jana (Weak)"
// Scenariusz: Użytkownik wylogowuje się (usuwamy referencję)
user = null;
// W tym momencie:
// W strongMap dane nadal istnieją (Garbage Collector ich nie ruszy)
// W weakMap dane zostaną usunięte automatycznie (przy najbliższym czyszczeniu pamięci)
2. Praktyczne zastosowanie: Prywatne dane
Często chcemy przypisać dodatkowe dane do obiektu (np. DOM elementu), ale nie chcemy modyfikować samego obiektu ani martwić się o wycieki pamięci, gdy element zostanie usunięty ze strony.
// Symulacja licznika odwiedzin dla obiektów użytkowników
const visitCount = new WeakMap();
function countUser(userObj) {
let count = visitCount.get(userObj) || 0;
visitCount.set(userObj, count + 1);
}
let user1 = { id: 1 };
countUser(user1);
console.log(visitCount.get(user1)); // 1
3. Zadanie dla chętnych
Stwórz strukturę, która "śledzi" aktywne sesje użytkowników. Jeśli obiekt sesji zostanie usunięty (np. przez pomyłkę lub timeout), WeakMap powinna przestać go pamiętać.
(Uwaga: W JS trudno "zobaczyć" moment działania Garbage Collectora w konsoli, ale ważna jest sama koncepcja).
[!TIP] Używaj
WeakMaptam, gdzie kluczami są obiekty, które mogą zniknąć w trakcie działania aplikacji (np. elementy HTML, tymczasowe instancje klas).
[!IMPORTANT] Commit: Zadanie 5.2 - WeakSet i WeakMap.
4. Więcej ćwiczeń (Samodzielne)
Ćwiczenie A: Cache Metadanych (WeakMap)
Wyobraź sobie system gry RPG. Masz obiekty reprezentujące postacie (character). Chcesz przechowywać dla nich tymczasowe statusy (np. "zatrucie", "błogosławieństwo"), ale nie chcesz "brudzić" głównego obiektu postaci dodatkowymi polami.
- Stwórz klasę lub obiekt fabrykujący
Character. - Stwórz
WeakMapo nazwiestatusEffects. - Napisz funkcję
applyStatus(character, status), która dodaje status do WeakMap. - Symulacja: Stwórz postać, nadaj jej status, sprawdź czy status istnieje. Następnie usuń postać (ustaw zmienną na
null) i uświadom sobie, że wpis wstatusEffectsteż zostanie posprzątany przez GC.
Ćwiczenie B: Oznaczanie przetworzonych wiadomości (WeakSet)
Masz system, który przetwarza wiadomości (obiekty). Każda wiadomość może być przetworzona tylko raz.
- Stwórz
WeakSeto nazwieprocessedMessages. - Stwórz tablicę obiektów
messages(np.{id: 1, text: "Witam"}, {id: 2, text: "Żegnam"}). - Napisz funkcję
process(message), która:- Sprawdza, czy wiadomość jest w
processedMessages. - Jeśli tak ->
console.log("Już było!"). - Jeśli nie ->
console.log("Przetwarzam...")i dodaje ją do zbioru.
- Sprawdza, czy wiadomość jest w
- Przetestuj funkcję wywołując ją dwa razy na tej samej wiadomości.
- Dlaczego
WeakSetjest tu lepszy niżSet? Bo gdy tablicamessageszostanie wyczyszczona, historia przetworzeń też zniknie z pamięci, nie blokując zasobów.