Destrukturyzacja, czyli co?
Destrukturyzacja to mechanizm, który istnieje nie tylko w JavaScript. Implementują go również inne języki programowania (np. Python i C#). Dzięki niemu można "wypakować" wartości z tablicy, lub obiektu bezpośrednio do zmiennych, a tym samym znacznie skrócić zapis i zwiększyć czytelność kodu.
Zamiast wielokrotnie odwoływać się najpierw do obiektu, a następnie do jego właściwości, możemy używać bezpośrednio właściwości, które wcześniej przypisaliśmy do zmiennych. Jest również kilka pułapek, na które trzeba uważać, ale o tym na samym końcu.
Podstawy
Jak wspomniałem destrukturyzacja dotyczy tablic (które w JavaScript są tak naprawdę obiektami) oraz obiektów. Na początek przyjrzymy się tym drugim.
W przykładzie powyżej utworzyłem trzy nowe zmienne (address
, age
, name
) używając destrukturyzacji. Teraz mogę używać ich bezpośrednio - nie muszę odwoływać się najpierw do obiektu person.name
lub person.age
.
To teraz po kolei przeanalizujmy co tu się wydarzyło:
użyłem słowa kluczowego
const
do zadeklarowania zmiennej (let
, orazvar
również zadziała).w nawiasach klamrowych (w przypadku tablic będą to nawiasy kwadratowe
[ ]
) wymieniłem po kolei właściwości obiektu na podstawie których chcę utworzyć zmienne. Ich nazwa musi odpowiadać tym w obiekcie person.ostatni krok to operator przypisania
=
. Bezpośrednio po nim umieściłem obiekt, który posiada te właściwości.
Składnia w przypadku tablic wygląda niemal identycznie. Szczegółowo opisuję ją w dalszej części artykułu przy okazji poznawania dodatkowych możliwości destrukturyzacji.
Referencja w destrukturyzacji
Przyjrzyjmy się kolejnemu przykładowi. Tym razem zastosujemy destrukturyzację bezpośrednio w parametrze funkcji. Składnia wygląda tu nieco inaczej - nie deklarujemy zmiennej, a jedynie wyciągamy wskazaną właściwość z obiektu.
Funkcja updateCity
zmienia właściwość city
obiekcie address
, który znajduje się wewnątrz obiektu person
przekazanego jako pierwszy argument. Bardzo ważną kwestią jest tutaj referencja. W przypadku wyciągnięcia za pomocą destrukturyzacji typu referencyjnego (np. obiektu lub tablicy) mamy do czynienia z oryginalnym obiektem, nie dochodzi do utworzenia kopii. W naszym przypadku wszystkie operacje (takie jak nadpisanie właściwości city) są dokonywane na obiekcie address
i to on zostanie zmodyfikowany.
Inaczej kwestia wygląda w przypadku typów prostych. Przyjrzyjmy się funkcji updateAge
. Tu również destrukturyzyjemy obiekt person
, jednak age
jest typem prostym. Mamy tu do czynienia z kopią wartości, a nie referencją do obiektu person
. Zatem jakiekolwiek operacje są dokonywane na nowej, niepowiązanej z obiektem person
wartości. Co za tym idzie, ta funkcja po prostu nie działa - obiekt person
nie zostanie zmodyfikowany.
Wiązanie this
Co prawda destrukturyzacja metod zdarza się niezwykle rzadko, jednak warto wiedzieć jak zachowa się program w takiej sytuacji.
Jeżeli metoda używa wewnątrz słowa kluczowego this
, to wywołanie jej w ten sposób spowoduje, że nie będzie ono wskazywać na obiekt, z którego pochodzi metoda. Powiązanie pomiędzy obiektem, a słowem this tworzy się dopiero w czasie działania aplikacji, dlatego kontekst wywołania metody ma spore znaczenie.
Rozwiązań tego problemu jest kilka:
możemy użyć metody
bind
do trwałego przypisania this do obiektu blogPost. Wymaga to jednak utworzenia dodatkowej zmiennej, która będzie przechowywać zbindowaną wersję metody.
podczas używania funkcji możesz stosować metody
call
lubapply
w celu manualnego wskazania kontekstuthis
Oczywiście rozwiązań jest znacznie więcej, ale te, które przedstawiłem stosuje się zdecydowanie najczęściej.
Jeżeli zdecydujesz się zdestrukturyzować metodę, to koniecznie sprawdź, czy wynik jej wywołania jest poprawny.
Wartości domyślne
Podczas destrukturyzowania tablicy lub obiektu możemy przypisać domyślne wartości do wyciąganych właściwości. Zobaczymy to na przykładzie tablicy.
Zapis wygląda tu niemal identycznie, jak podczas destrukturyzacji obiektu. Jedyna różnica, to nawiasy kwadratowe zamiast klamrowych.
Nowością jest tutaj dodanie operatora przypisania =
wraz z domyślną wartością. Dokładnie ten sam zapis możemy zastosować w parametrach funkcji. Należy pamiętać, że domyślna wartość zostanie użyta tylko wtedy, gdy w miejscu parametru znajdzie się wartość undefined
(nie zadziała to w przypadku null
).
Z tego powodu w przykładzie poniżej mimo, że przypisałem domyślną do czwartego elementu, to nie zostanie ona zastosowana. Tak samo sytuacja wygląda w przypadku domyślnych wartości dla parametrów funkcji.
Domyślne wartości można stosować zarówno podczas destrukturyzacji obiektów jak i tablic, a wartość, która zostanie tam przypisana zależy tylko od Ciebie. W przykładzie powyżej domyślną wartością jest wywołanie funkcji. logMissingPerson
wykona się tylko w momencie, kiedy wartością będzie undefined
.
Kolizja nazw
Czasami może się okazać, że zmienna o takiej samej nazwie istnieje w danym kontekście. W takim przypadku najłatwiej jest zmienić nazwę destrukturyzowanej właściwości i uniknąć kolizji nazw.
Sam zapis nie jest skomplikowany - nadal używamy nazwy właściwości takiej, jak w obiekcie person, jednak zaraz po niej stosujemy dwukropek :
, a tuż po nim nadajemy nową nazwę.
Ta zmiana nie wpływa na obiekt person
, a jedynie tworzy nową zmiennej o tej nazwie. Zatem oryginalny obiekt pozostaje niezmieniony.
Zmiana nazwy może być połączona z wartością domyślną, chociaż sam zapis nie jest czytelny na pierwszy rzut oka. Podobnie jak poprzednio najpierw za pomocą dwukropka zmieniamy nazwę, a następnie z użyciem operatora przypisania =
nadajemy wartość domyślną.
Operator rest
Podczas destrukturyzacji możemy posłużyć się operatorem rest ...
w celu "zebrania" wszystkich pozostałych wartości. Operator ten zawsze musi znaleźć się na samym końcu (podobnie jak w parametrach funkcji).
Zastosowałem tu podobny zapis, co poprzednio, jednak na końcu dodałem ...personWithoutName
(nazwa jest tutaj dowolna i zależy tylko od Ciebie). Pod tą wartością znajdują się wszystkie właściwości z obiektu person
, które nie zostały wcześniej zdestrukturyzowane i przypisane do zmiennych.
Ponieważ wcześniej wyciągnęliśmy do zmiennej jedynie name
, to do personWithoutName
trafił cały obiekt, poza tą jedną właściwością. Nic jednak nie stoi na przeszkodzie, żeby wyciągnąć wcześniej więcej niż jedną rzecz, wtedy operator rest
zbierze pozostałe właściwości.
Zaawansowana destrukturyzacja
Wiesz już prawie wszystko na temat destrukturyzacji. Zostały nam jeszcze do poruszenia dwie kwestie - zagnieżdżona destrukturyzacja oraz zastosowanie przecinka ,
w celu pominięcia niektórych wartości.
Zamiast poznawać je po kolei, tak jak inne zagadnienia, posłużymy się tym razem bardziej zaawansowanym przykładem i zastosujemy wszystkie poznane do tej pory techniki, a przy okazji wprowadzimy dwie nowe.
Czytelność w tym przykładzie nie jest na najwyższym poziomie - bardzo rzadko zajdzie potrzeba stosowania wszystkich możliwości jednocześnie. Skupmy się jednak na tym, jakie nowości się tutaj pojawiły:
Podczas destrukturyzacji tablicy people zastosowałem
,
(przecinek) w celu pominięcia pierwszego elementu w tablicy, czyli obiektu z właściwościąname: "Alicja"
. Gdybyś chciał pominąć więcej niż jeden element, to możesz zastosować kilka przecinków z rzędu. Zapis wyglądałby następująco:[,,, ...rest] = someArr
(pomijamy 3 pierwsze elementy). W przypadku destrukturyzowania obiektu, ta składnie nie ma zastosowania, ponieważ tam po prostu wskazujemy konkretne klucze, które chcemy wyciągnąć.Drugą nowością jest destrukturyzacja zagnieżdżonego obiektu
address
. Zamiast wyciągać cały obiekt, wchodzimy nieco głębiej i destrukturyzujemy obiekt umieszczony wewnątrz innego obiektu. Nie ma ograniczeń co do głębokości destrukturyzacji, ale zalecam zachować umiar. Zbyt głęboka destrukturyzacja moża być trudna do odczytania.
Ostatni temat, który pozostał do omówienia, to dynamiczna destrukturyzacja. Najłatwiej będzie to zrozumieć na przykładzie:
Składnia jest tu trochę inna niż poprzednio. Żeby użyć wyrażenia jako klucz musimy zastosować nawiasy kwadratowe [ ]
, wewnątrz umieszczamy parametr key
, czyli nazwę klucza, która będzie przekazana podczas wywołania funkcji. Ostatni krok to nadanie nowej nazwy za pomocą [key] : newName
. Zdestrukturyzowana wartość znajduje się pod zmienną o nazwie newName
(w przykładzie powyżej jest to property
).
Podsumowanie
Temat destrukturyzacji w JavaScript został wyczerpany niemal w 100%. Nie musisz uczyć się na pamięć wszystkich możliwości, ale na pewno warto wiedzieć o ich istnieniu. Jeżeli w którymś momencie będziesz potrzebował odświeżyć wiedzę, to zachęcam do ponownego przeczytania tego artykułu lub zajrzenia do dokumentacji w MDN.
Pamiętaj również o zachowaniu zdrowego rozsądku - jeżeli obiekt posiada wiele zagnieżdżonych właściwości, które są użyte tylko raz (np. w funkcji), to prawdopodobnie nie ma sensu ich wszystkich wyciągać, a próba pójścia tą drogą może obniżyć czytelność kodu.
Jednak w większości przypadków destrukturyzacja znacznie zwiększa przejrzystość kodu, dlatego zachęcam do jej używania i eksperymentowania. Z całą pewnością doświadczenie jest tutaj bardzo ważnym czynnikiem, który znacznie ułatwia wyznaczanie granic, a jedynym sposobem na zdobycie go, jest stosowanie tej (i innych) techniki w praktyce.