Map i Set
Map i Set to dwie struktury danych, które są czymś pomiędzy tablicami i klasycznymi obiektami.
Map()
Mapy służą do tworzenia zbiorów z parami [klucz - wartość]. Przypominają one klasyczne obiekty (czy np. mapy z SASS), natomiast główną różnicą odróżniającą je od klasycznych obiektów, jest to, że kluczami może być tutaj dowolny typ danych.
Aby stworzyć mapę możemy skorzystać z jednej z 2 konstrukcji:
const map = new Map();
map.set("kolor1", "red");
map.set("kolor2", "blue");
//lub
const map = new Map([
["kolor1", "red"],
["kolor2", "blue"],
]);Dla każdej mapy mamy dostęp do kilku metod:
get(key)
Zwraca wartość danego klucza
has(key)
Sprawdza czy mapa ma dany klucz
delete(key)
Usuwa dany klucz i zwraca true/false jeżeli operacja się udała
clear()
Usuwa wszystkie elementy z mapy
entries()
Zwraca iterator zawierający tablicę par [klucz-wartość]
keys()
Zwraca iterator zawierający listę kluczy z danej mapy
values()
Zwraca iterator zawierający listę wartości z danej mapy
forEach
robi pętlę po elementach mapy
prototype[@@iterator]()
Zwraca iterator zawierający tablicę par [klucz-wartość]
Aby pobrać długość mapy, użyjemy właściwości size:
Klucze w mapie
Mapy w przeciwieństwie do obiektów mogą mieć klucze dowolnego typu, gdzie w przypadku obiektów (w tym tablic) są one konwertowane na tekst:
W przypadku klasycznych obiektów, klucze zawsze są konwertowane na tekst (obiekty na zapis [object Object]:
Pętla po mapie
Jeżeli będziemy chcieli iterować po mapie, możemy wykorzystać pętlę for of i poniższe funkcje zwracające iteratory:
keys()
Zwraca tablicę kluczy
values()
Zwraca tablicę wartości
Do iterowania możemy też wykorzystać wbudowaną w mapy funkcję forEach:
Set()
Obiekt Set jest kolekcją składającą się z unikalnych wartości, gdzie każda wartość może być zarówno typu prostego jak i złożonego. W przeciwieństwie do mapy jest to zbiór pojedynczych wartości.
Żeby stworzyć Set możemy użyć jednej z 2 składni:
Obiekty Set mają podobne właściwości i metody co obiekty typu Map, z małymi różnicami:
clear()
Usuwa wszystkie elementy ze zbioru
delete(key)
Usuwa dany klucz i zwraca true/false jeżeli operacja się udała
entries()
Zwraca iterator zawierający tablicę par [klucz-wartość]
has(key)
Sprawdza czy mapa ma dany klucz
keys()
Zwraca iterator zawierający listę kluczy z danej mapy
values()
Zwraca iterator zawierający listę wartości z danej mapy
forEach
robi pętlę po elementach mapy
prototype[@@iterator]()
Zwraca iterator zawierający tablicę par [klucz-wartość]
W przypadku Set() klucze i wartości są takie same, dlatego robiąc pętle nie ważne czy użyjemy powyższych values(), keys(), entries() czy po prostu zrobimy pętlę for of:
Set i tablice
Dzięki temu, że Set zawiera niepowtarzające się wartości, możemy to wykorzystać do odsiewania duplikatów w praktycznie dowolnym elemencie iteracyjnym - np. w tablicy:
To samo tyczy się oczywiście dynamicznie tworzonych setów:
WeakMap()
WeakMap to odmiana Mapy, którą od Map rozróżniają trzy rzeczy:
Nie można po niej iterować (w przyszłości będzie można, bo już zapowiedziano odpowiednie zmiany)
Kluczami mogą być tylko obiekty
Jej elementy są automatycznie usuwane gdy do danego obiektu (klucza) nie będzie referencji
Aby stworzyć nową WeakMap, skorzystamy z instrukcji:
Każda mapa daje nam kilka metod:
get(key)
Pobiera wartość klucza
has(key)
Zwraca true/false w zależności czy dana WeakMap posiada klucz o danej nazwie
delete(key)
Usuwa wartość przypisaną do klucza
W odróżnieniu do Map elementy WeakMap są automatycznie usuwane jeżeli do danego obiektu/klucza nie będzie żadnych referencji.
Co to oznacza? W rozdziale o Garbage Collection dyskutowaliśmy o zarządzaniu pamięcią i tym, że jeżeli na dany obiekt wskazuje jakakolwiek referencja, nie może on być usunięty z pamięci. Spójrzmy jeszcze raz na tamten przykład:
Jak widzisz, mimo, że zmienna ob została ustawiona na null, obiekt nie został usunięty z pamięci, ponieważ wciąż wskazuje na niego pierwszy indeks tablicy tab[0]. Podobna sytuacja będzie w przypadku Map:
Co zresztą nie jest dziwne. W przypadku WeakMap taki element zostanie automatycznie z niej usunięty:
Dzięki czemu obiekt będzie mógł być usunięty z pamięci, bo nie będzie na niego wskazywać żadna referencja.
Uwaga. Powyższe console.log pokaże nam w debuggerze WeakMap z elementem, którego nie powinno być. Wynika to z tego, że przy czyszczeniu pamięci działają pewne mechanizmy optymalizacyjne, które sprawiają, że nie musi on być odpalany przy każdej linijce kodu. Stąd w debuggerze nie zobaczysz wyniku usunięcia nieużytecznych obiektów.
Żeby realnie sprawdzić działanie takiego kodu, trzeba by wymusić odpalenie w danym momencie Garbage Collectora. Można to zrobić na kilka sposobów.
Gdzie to może się przydać? W zasadzie wszędzie, gdzie będziemy przeprowadzać dodatkowe operacje na obiektach, które potencjalnie mogą być zaraz usunięte. Wyobraźmy sobie dla przykładu, że w jednym ze skryptów mamy naście obiektów reprezentujących pliki.
Chcielibyśmy teraz napisać funkcję, która będzie do zmiennej readCount zbierać informacje o liczbie przeczytań danego pliku:
Wynik w zmiennej readCount wyszedł nam jak należy.
Problem z powyższym kodem jest ten sam co opisywany wcześniej. Na takich plikach mogą być przeprowadzane różne operacje. Może jakiś fragment kodu usunie dany plik? W takiej sytuacji nie powinniśmy już trzymać jego danych.
Niestety w przypadku Map (ale i tablic czy obiektów) referencje będą trzymane do czasu, aż ich ręcznie z nich nie usuniemy. A to oznacza, że do powyższego kodu powinniśmy dorobić funkcjonalność, która usuwała by dane o nieistniejącym już pliku. Powiedzmy, że nie wiemy gdzie, albo nie możemy modyfikować fragmentu, który usuwa dane pliki. Jak sprawić by powyższy zbiór readCount automatycznie się aktualizował?
I tutaj właśnie pojawia się zaleta WeakMap, w przypadku których odpowiednie wpisy będą automatycznie usuwane, gdy dany obiekt zostanie gdzieś usunięty i nie będzie już referencji do niego:
WeakSet()
Podobnie jak dla Map istnieją WeakMap, tak dla Setów istnieją WeakSet. Są to kolekcje składające się z unikalnych obiektów. Podobnie do WeakMap obiekty takie będą automatycznie usuwane z WeakSet, jeżeli do danego obiektu zostaną usunięte wszystkie referencje.
Każdy WeakSet udostępnia nam metody:
delete(ob)
Usuwa dany obiekt z kolekcji
has(ob)
Zwraca true/false w zależności, czy dana kolekcja zawiera dany obiekt
WeakSet idealnie nadaje się do zbierania w jeden zbiór obiektów, które potencjalnie w dalszej części skryptu mogą zostać usunięte, a więc nie powinny być trzymane w naszej "liście":
Last updated