Krótka opowieść o switchach i alternatywach w JavaScripcie

Lubicie instrukcje warunkowe? Ja też nie… Są super i tak dalej, ale po napisaniu trzeciego z kolei else if powinniście zatrzymać się na chwilę i zastanowić, co robicie ze swoim życiem. Gdyby tylko istniał lepszy sposób na wykonanie konkretnego bloku kodu w zależności od zmiennej… Dobra, żarty na bok i przechodzimy do konkretów. 

Dawid RogowiczFrontend developer w firmie Boldare. Uwielbia tworzyć aplikacje webowe przy użyciu Reacta, najlepiej w połączeniu z Typescriptem. Nie znosi stylowania, ale jak już ma to robić to tylko CSSinJS! Jest fanem zagłębiania się w niskopoziomowe zagadnienia i uczenia się jak coś działa „pod spodem”, a nie tylko jak tego użyć. Po godzinach gra na gitarze/basie, a weekendy lubi spędzić w garażu grzebiąc przy aucie.


Rozdział pierwszy: „The Switch Situation”

Nie będę tutaj wchodzić w szczegóły, przypomnę tylko pokrótce, jak wygląda składnia konstrukcji switch:

Jak widać, jest ona jasna i czytelna. Silniki przeglądarek różnie rozwiązują kwestię optymalizacji, ale najważniejsze jest to, że jeśli twój kod spełnia warunek znajdujący się na samym końcu konstrukcji switch, to nie musisz czekać, aż pozostałe warunki zostaną sprawdzone – od razu wykona się właściwa instrukcja. To oznacza, że liczba zdefiniowanych warunków nie ma wpływu na szybkość działania skryptu. Możemy też wielokrotnie wykonać tę samą operację sprawdzania dla różnych warunków, zachowując czytelność kodu.

Rozdział drugi: „Zapomniane opowieści”

Wiemy już, czym są instrukcje switch i jakie są ich plusy i minusy, ale switch to tylko jedna z możliwości rozwiązania naszego problemu. Podsumujmy krótko, jakie inne opcje mamy do dyspozycji:

Instrukcja if/else

Tej akurat możliwości staraj się unikać. if/else wprowadza bałagan w kodzie i spowalnia działanie skryptu. Jeżeli warunek, który sprawdzasz, jest uznany za prawdziwy (true) dopiero w dziesiątej z kolei instrukcji, to tak czy inaczej wszystkie wcześniejsze warunki muszą zostać sprawdzone. W większości sytuacji nie będzie to przeszkodą, jednak im więcej warunków, tym skrypt wolniejszy i mniej czytelny. Jeżeli na przykład używamy pętli, możemy pozbyć się wyrażenia else i wstawić wewnątrz każdego if instrukcję return, która natychmiast przerwie wykonywanie kodu, gdy tylko warunek zostanie spełniony.

Obiekty

Tak, nie przesłyszeliście się: obiekty! Chociaż generalnie służą one do czegoś innego, możemy “zhakować” je tak, żeby pasowały do naszych potrzeb. Mimo że instrukcja switch zawsze będzie na miejscu i znakomicie pasuje do imperatywnego stylu programowania, to jeżeli tak jak ja należycie do tych świrów, którzy uważają, że każda kolejna zadeklarowana zmienna to zło, pewnie będziecie chcieli ją jakoś ulepszyć. Oto jak można wykorzystać obiekty, aby wyglądały niemal jak programowanie funkcyjne:

W tym przykładzie przypisujemy funkcje do poszczególnych kluczy, aby je wywołać. Jeżeli żądany klucz nie zostanie znaleziony w obiekcie, możemy też stworzyć domyślną funkcję, która zostanie uruchomiona w takiej sytuacji.

Tablice

Mieliśmy już obiekty, dlaczego więc zatem nie spróbować z tablicami? Możemy dostać się do konkretnego elementu w tablicy za pomocą jego indeksu, więc tablica zadziała podobnie jak obiekt – z tą różnicą, że zamiast nazwy klucza będziemy mieli liczbę.

Rozdział trzeci: Optymalizacja

Mam nadzieję, że lubicie matmę, liczby i kolorowe wykresy, bo mam dla was coś wyjątkowego. Przygotowałem benchmarki omówionych rozwiązań – możecie obejrzeć je tutaj i sprawdzić, które działa najszybciej na waszych komputerach.

W każdym teście przypisuję wartość do y bazując na stałej x, gdzie x jest zawsze umieszczony w ostatnim bloku if/case/właściwości, etc., żeby sprawdzić najgorszy scenariusz.

Tablica

Jak widać (albo i nie), wywoływanie funkcji wprost z tablicy nie jest najszybszym sposobem. Mówiąc wprost, jest on tak wolny, że nawet nie widać go na wykresie. To chyba najgorsze z omówionych rozwiązań: możemy używać tylko liczb, aby odwołać się do elementów tablicy, a co gorsza, te elementy mogą zmieniać swoją kolejność. Test prędkości mówi sam za siebie – odradzam!

Sytuacja jest jednak inna, gdy w tablicy używamy typów prymitywnych (górny czerwony pasek). Ku mojemu zaskoczeniu, ta opcja uzyskała najlepszy wynik w Chrome, a w przypadku Firefoksa była przynajmniej widoczna na wykresie – czy to znaczy, że warto jej używać? Powiedziałbym, że nie – tablice nie są przeznaczone do tego celu. Ale hej, na tym właśnie polega hakowanie.

Instrukcje if

Zarówno if (ciemnozielony pasek) jak i if/else (pomarańczowy pasek) uzyskały całkiem przyzwoity wynik. Powinny być one jednak dla nas punktem odniesienia, więc nie pozwólmy im jeszcze spocząć na laurach. Warto tutaj zauważyć, że instrukcje z else nie są wcale wolniejsze. Oczywiście wynik będzie zależał od silnika przeglądarki i konkretnego kodu, ale w tym przykładzie uzyskałem właśnie taki rezultat. Jako że wylałem już sporo żółci na używanie wielu ifów jeden po drugim, możemy zakończyć ten temat i przejść dalej.

Instrukcje switch

Tu zaczyna się zabawa. Przygotowałem trzy testy dla instrukcji switch. W dwóch z nich w sprawdzanych warunkach używam liczb (fioletowa linia) i stringów (jasnozielona linia) – oba przypadki widoczne są na obrazku powyżej.

Liczby wypadają lepiej w silnikach SpiderMonkey (Firefox) i V8 (Chromium). Czy to znaczy, że warto używać tego sposobu? Raczej nie, biorąc pod uwagę, że i tak trzeba przypisać te liczby do jakichś konkretnych wartości. W przypadku stringów natomiast kod jest znacznie bardziej przejrzysty, co ułatwia jego późniejsze utrzymanie.

Różowy pasek na wykresie pokazuje przypadek, gdzie przypisałem switch do funkcji i zwróciłem jej wartość. Wynik funkcji natomiast przypisałem do zmiennej y. Ten test miał sprawdzić, czy zwracanie wartości ze switcha będzie szybsze niż przerywanie wykonywania kodu za pomocą break. Jak widać, raczej nie.

Ostatni benchmark (ciemnoczerwona linia) to switch, który w każdym przypadku zwraca wartość (podobnie jak robiliśmy to wcześniej z tablicą). Ponieważ używamy return, nie ma potrzeby za każdym razem przerywać kodu wyrażeniem break. Wychodzi na to, że lepiej jest przetwarzać dane wewnątrz poszczególnych bloków case, zamiast próbować zawężać liczbę bloków do tych, które zawierają tylko unikalne zmienne.

Epilog: „Coś się kończy, coś się zaczyna”

Po tych wszystkich wyszukanych kolorach i liczbach, czas zebrać przemyślenia w całość i spróbować je podsumować. Jeżeli chodzi o szybkość wykonania kodu, to najlepszym wyborem będzie instrukcja if lub switch. Obiekty i tablice są po prostu zbyt wolne, i to do tego stopnia, że możemy całkowicie je wyeliminować. Co prawda tablice z typami prymitywnymi osiągnęły najlepszy wynik w Chrome, ale nie rekompensuje to ich wad, omówionych wcześniej.

Zostaje nam więc if i switch. Cóż… w zasadzie to zostaje tylko switch! Jeżeli mamy do sprawdzenia tylko dwa lub trzy warunki, if wystarczy, ale większa liczba przypadków zdecydowanie powinna być obsługiwana przez switch. Switch właśnie po to został stworzony, i nasz kod będzie o wiele bardziej czytelny.

A skoro już jesteśmy przy temacie czytelności kodu, to wspomniałem już, że przy sprawdzaniu warunków lepiej używać stringów niż liczb, nawet jeżeli ta druga opcja jest szybsza. W rzeczywistości różnica będzie niemal niezauważalna, więc lepiej skupić się na tym, by kod był czysty i łatwy w utrzymaniu.

Jaki jest wniosek z tego wszystkiego? Nie możemy w jeden wieczór pobić lat doświadczenia developerów Firefoksa i Chromium. Dzięki za uwagę!


Artykuł został pierwotnie opublikowany na boldare.com. Zdjęcie główne artykułu pochodzi z unsplash.com.

Zapraszamy do dyskusji

Patronujemy

 
 
Polecamy
Ofertę pracy znalazłem w opisie filmu na YT. Krzysztof Woliński o pracy w Japonii