Gdy aplikacja webowa ma reagować na kilka konkretnych stanów, ról użytkownika albo typów zdarzeń, seria instrukcji if...else szybko staje się trudna do czytania. Konstrukcja switch pozwala uporządkować taki kod, ale tylko wtedy, gdy rozumiesz działanie case, break i default. Pokażę składnię, praktyczne przykłady, typowe błędy oraz sytuacje, w których lepiej wybrać if...else albo mapę obiektów.
Najważniejsze zasady używania switch w JavaScript
- switch porównuje jedną wartość z kolejnymi przypadkami za pomocą ścisłego porównania.
-
break zatrzymuje wykonanie, aby kod nie przeszedł przypadkowo do następnego
case. - default obsługuje wartości, których nie przewidziano wcześniej.
- Grupowanie case dobrze sprawdza się wtedy, gdy kilka wartości prowadzi do tej samej akcji.
- if...else jest czytelniejsze przy zakresach i złożonych warunkach.

Jak działa switch w JavaScript
switch przyjmuje jedną wartość, a potem porównuje ją z wyrażeniami zapisanymi przy kolejnych etykietach case. JavaScript używa tu odpowiednika operatora ===, więc typ wartości ma znaczenie. Tekst "1" nie pasuje do liczby 1, co jest jednym z częstszych źródeł niespodzianek.
Podstawowa konstrukcja wygląda tak:
switch (wartosc) {
case "a":
// instrukcje dla wartości "a"
break;
case "b":
// instrukcje dla wartości "b"
break;
default:
// obsługa nieznanej wartości
}Po znalezieniu pasującego przypadku wykonywanie zaczyna się od jego instrukcji. Bez break kod przejdzie dalej i wykona również kolejne przypadki. Dokumentacja MDN opisuje to zachowanie jako fall-through, czyli przejście wykonania do następnych etykiet.
| Element | Rola | Praktyczna uwaga |
|---|---|---|
switch |
Wskazuje wartość podlegającą sprawdzeniu. | Najlepiej używać go dla jednej zmiennej lub jednego wyrażenia. |
case |
Definiuje konkretną wartość. | Porównanie jest ścisłe, bez automatycznej konwersji typu. |
break |
Kończy wykonanie konstrukcji. | Nie jest potrzebny przed return albo throw. |
default |
Obsługuje brak dopasowania. | Przydaje się jako bezpieczna ścieżka dla nieznanych danych. |
Traktuję default jako mały bezpiecznik. Dane z API, parametr adresu URL albo wartość przechowywana w bazie mogą kiedyś przyjąć nową postać. Dzięki temu interfejs nie kończy się pustym komunikatem tylko dlatego, że backend zwrócił status, którego frontend jeszcze nie zna.
Podstawowy przykład w aplikacji webowej
Dobrym zastosowaniem jest wyświetlanie komunikatu zależnego od statusu zamówienia. W tym przypadku mamy jedną wartość i kilka jasno określonych możliwości, więc switch pozostaje czytelny nawet po dodaniu kolejnych stanów.
function getOrderMessage(status) {
switch (status) {
case "new":
return "Zamówienie zostało przyjęte.";
case "paid":
return "Płatność została zaksięgowana.";
case "shipped":
return "Przesyłka jest już w drodze.";
case "cancelled":
return "Zamówienie zostało anulowane.";
default:
return "Status zamówienia jest nieznany.";
}
}
const message = getOrderMessage("shipped");
console.log(message);W tym przykładzie każde case kończy się przez return, więc nie trzeba dodawać break. Funkcja natychmiast zwraca wynik i opuszcza swoje wykonanie. To jeden z moich ulubionych sposobów zapisu, ponieważ ogranicza ryzyko przypadkowego fall-through.
Podobny mechanizm można wykorzystać przy renderowaniu widoku, wyborze komunikatu walidacyjnego albo obsłudze odpowiedzi HTTP. Trzeba jednak pamiętać, że sama konstrukcja nie wykonuje żadnego mapowania automatycznie. To programista decyduje, co ma się stać dla każdego stanu.
function getResponseAction(statusCode) {
switch (statusCode) {
case 200:
return "Pokaż dane użytkownika.";
case 401:
return "Przekieruj do logowania.";
case 403:
return "Pokaż komunikat o braku uprawnień.";
case 404:
return "Pokaż stronę nie znaleziono.";
case 500:
return "Pokaż komunikat o błędzie serwera.";
default:
return "Obsłuż nieznaną odpowiedź.";
}
}Ten wariant działa dobrze, gdy lista kodów jest krótka i zamknięta. Jeżeli przypadków zaczyna być kilkanaście, a każdy uruchamia rozbudowaną logikę, lepiej przenieść poszczególne operacje do osobnych funkcji. Sam switch nie powinien stawać się wielką procedurą z logiką biznesową, obsługą formularza i komunikacją z API w jednym miejscu.
break, fall-through i kilka wartości dla jednej akcji
Najczęstszy błąd wygląda niewinnie. Programista dopisuje kod w jednym case, zapomina o break i nagle po jednej akcji uruchamia się kod przeznaczony dla kolejnego przypadku.
const role = "user";
switch (role) {
case "user":
console.log("Panel użytkownika");
case "admin":
console.log("Panel administracyjny");
break;
default:
console.log("Brak dostępu");
}Dla wartości "user" zostaną wypisane oba komunikaty. JavaScript nie traktuje kolejnego case jak automatycznej granicy bloku. W praktyce dodaję break od razu po zakończeniu każdej niezależnej gałęzi, chyba że przejście dalej jest świadomą decyzją.
Brak break może być celowy, gdy kilka wartości ma prowadzić do tej samej instrukcji. Wtedy zapisujemy przypadki jeden pod drugim:
function getLanguageMessage(language) {
switch (language) {
case "pl":
case "en":
return "Dostępna jest wersja strony.";
case "de":
return "Die deutsche Version ist verfügbar.";
default:
return "Wybrany język nie jest obsługiwany.";
}
}W tym przykładzie "pl" i "en" są grupą. Żaden z tych przypadków nie ma własnych instrukcji, więc wykonanie dochodzi do wspólnego return. Taki zapis jest czytelny, ale dobrze zostawić krótką uwagę w komentarzu, jeśli grupowanie nie wynika od razu z kodu.
Istnieją też sytuacje, w których fall-through wykonuje kilka etapów po kolei. To jednak rozwiązanie dla doświadczonych osób i wymaga komentarza wyjaśniającego intencję. Bez tego kolejny programista może potraktować brak break jak zwykłą pomyłkę i „naprawić” działający kod.
switch czy if...else albo mapa obiektów
Nie każda seria warunków potrzebuje switch. Ja wybieram tę konstrukcję wtedy, gdy sprawdzam jedną wartość względem kilku konkretnych możliwości. Przy zakresach, operatorach logicznych i warunkach zależnych od kilku zmiennych zwykle lepiej wypada if...else.
| Rozwiązanie | Najlepsze zastosowanie | Ograniczenie |
|---|---|---|
switch |
Statusy, role, typy zdarzeń i kody odpowiedzi. | Słabiej radzi sobie z zakresami i wieloma warunkami. |
if...else |
Warunki typu „większe niż”, przedziały i złożone wyrażenia. | Przy wielu równych porównaniach kod może stać się rozwlekły. |
| Mapa obiektów | Proste przypisanie wartości do etykiet, funkcji albo konfiguracji. | Nie zawsze pasuje do rozbudowanej logiki i efektów ubocznych. |
Przykład warunku zakresowego jest prostszy z użyciem if...else:
function getDiscount(points) {
if (points >= 1000) {
return 20;
}
if (points >= 500) {
return 10;
}
return 0;
}Próba zapisania tego jako zwykłych przypadków byłaby błędem. Konstrukcja case points >= 1000 porównywałaby liczbę punktów z wartością logiczną true, a nie sprawdzała zakres w sposób, którego można intuicyjnie oczekiwać.
Jeśli potrzebujesz wyłącznie prostego słownika, mapa obiektów bywa krótsza:
const labels = {
pending: "Oczekuje na płatność",
paid: "Opłacone",
shipped: "Wysłane"
};
function getLabel(status) {
return labels[status] ?? "Nieznany status";
}Mapa dobrze oddziela dane od logiki. Z drugiej strony switch daje więcej miejsca na walidację, wywołanie funkcji, zapis do logów albo różne działania dla każdej wartości. Nie kierowałbym się więc samą długością kodu. Ważniejsze jest to, czy czytelnik od razu rozumie, czy mamy mapowanie danych, czy proces decyzyjny.
Typowe błędy i praktyki, które poprawiają kod
Brak break w niezależnym przypadku
Jeżeli gałąź nie kończy się przez return, throw albo inną świadomą konstrukcję, dodaj break. Błąd jest szczególnie zdradliwy przy kodzie interfejsu, ponieważ efekt może pojawić się dopiero po wybraniu konkretnej kombinacji danych.
Nieprawidłowy typ wartości
Wartość pobrana z formularza lub adresu URL zwykle jest tekstem. Dlatego switch (input) nie dopasuje case 1, jeśli input ma wartość "1". Zanim zaczniesz szukać problemu w samym case, sprawdź typ przez typeof i ujednolić dane na wejściu.
const page = Number(new URLSearchParams(location.search).get("page"));
switch (page) {
case 1:
console.log("Pierwsza strona");
break;
default:
console.log("Inna strona");
}Brak obsługi wartości nieznanych
default nie zawsze jest formalnie wymagany, ale w aplikacjach webowych zwykle warto go dodać. Może zwrócić bezpieczny komunikat, uruchomić logowanie albo zatrzymać dalsze przetwarzanie. Szczególnie niebezpieczne jest ciche pomijanie nowego statusu zwróconego przez API.
Próba użycia zakresów w case
Standardowy switch nie jest konstrukcją do sprawdzania przedziałów. W takich sytuacjach można spotkać wzorzec switch (true), ale zwykle nie poprawia on czytelności:
switch (true) {
case score >= 90:
return "bardzo dobry";
case score >= 60:
return "dobry";
default:
return "wymaga poprawy";
}Ten zapis działa, ponieważ kolejne przypadki zwracają wartości logiczne, które są porównywane z true. Mimo to przy kilku progach częściej wybrałbym if...else. Jest bardziej oczywiste dla osoby, która utrzymuje kod po kilku miesiącach.
Przeczytaj również: Bootstrap spinner - jak wdrożyć ładowanie i dostępność
Zmienne deklarowane bez własnego bloku
Etykiety case nie tworzą automatycznie osobnego zakresu dla let i const. Jeżeli w kilku przypadkach deklarujesz zmienne o tych samych nazwach, możesz otrzymać błąd składni. Rozwiązaniem jest zamknięcie instrukcji danego przypadku w nawiasach klamrowych.
switch (status) {
case "ready": {
const message = "Gotowe";
console.log(message);
break;
}
case "error": {
const message = "Wystąpił błąd";
console.log(message);
break;
}
default: {
console.log("Nieznany status");
}
}Nie stosuję dodatkowych bloków bez powodu, ale przy większych przypadkach poprawiają one izolację zmiennych. To drobny szczegół, który oszczędza później czasu podczas refaktoryzacji.
Jak pisać switch, który dobrze znosi rozwój aplikacji
Najprostsza zasada brzmi: każdy przypadek powinien mieć jedną wyraźną odpowiedzialność. Jeśli jedna gałąź aktualizuje widok, zapisuje dane i wysyła żądanie do serwera, przenieś te operacje do osobnej funkcji. Wtedy switch wybiera działanie, ale nie przejmuje całej logiki aplikacji.
Przy większych projektach dobrze jest również ustalić, jakie wartości są dozwolone. Statusy takie jak "pending", "paid" i "shipped" powinny pochodzić z jednego uzgodnionego modelu, a nie być wpisywane ręcznie w kilkunastu komponentach. Ogranicza to literówki, które w przypadku stringów nie zawsze są widoczne podczas przeglądania kodu.
Dobrym testem jakości jest sprawdzenie trzech sytuacji: każdego znanego przypadku, wartości nieznanej oraz wartości o nieprawidłowym typie. Jeśli funkcja zachowuje się rozsądnie we wszystkich trzech, konstrukcja jest znacznie bardziej odporna na zmiany po stronie formularza, API i interfejsu.
Nie próbowałbym też na siłę skracać dużego switch. Dwanaście prostych przypadków może być czytelniejsze niż skomplikowana mapa funkcji, a trzy przypadki z rozbudowaną logiką mogą uzasadniać osobne funkcje. Liczba gałęzi jest wskazówką, ale o wyborze powinien decydować charakter problemu.
Najlepszy switch to ten, którego intencję widać od razu
switch sprawdza się przy skończonej liczbie jasno nazwanych wartości. Zapamiętaj trzy rzeczy: porównanie jest ścisłe, break chroni przed przypadkowym przejściem dalej, a default obsługuje świat, który zwykle jest większy niż nasze założenia.
W codziennym kodzie aplikacji webowej nie wybieram tej konstrukcji dlatego, że jest krótsza od if...else. Wybieram ją wtedy, gdy pokazuje decyzję w sposób naturalny. Jeśli po kilku sekundach wiadomo, jaka wartość prowadzi do jakiej akcji, rozwiązanie jest prawdopodobnie dobrze dopasowane.
