Instrukcje warunkowe if oraz else if

Instrukcje warunkowe to jeden z częściej używanych mechanizmów w każdym języku programowania. W artykule przeanalizujemy składnię i kilka drobnych niuansów związanych z instrukcjami if.

Instrukcje warunkowe służą do określenia konkretnych przypadków, gdy jakieś działanie może zostać podjęte. Przyjmijmy na przykład, że chcemy napisać funkcję Division (ang. dzielenie), która przyjmuje dwa argumenty (a, b) i zwraca wynik dzielenia liczby a przez liczbę b. Teoretycznie zadanie jest proste, ale co jeśli funkcja zostanie wywołana z parametrem b równym zero? W JavaScript nie zostanie zgłoszony błąd, lecz jako wynik zwrócona zostanie specjalna wartość „Infinity”. Użytkownik powinien jednak otrzymać bardziej „przyjazną” informację, np. komunikat „Błąd – nie można dzielić przez zero!”. Spójrzmy zatem jak wykorzystać w tym celu instrukcję warunkową:

function Division(a,b) {
    if (b === 0) { 
        return 'Błąd - nie można dzielić przez zero!'; 
    }
    return a / b;
}

Division(4,2); //2
Division(4,0); // Błąd - nie można dzielić przez zero!

Tyle tytułem wstępu i omówienia czym w zasadzie są instrukcje warunkowe. Ja widać są one przydatne i niewątpliwie będziemy je stosować praktycznie w każdym kodzie.

Instrukcja if może zostać rozbudowana o dwa dodatkowe elementy: else oraz else if. Instrukcja else służy do wskazania sytuacji, której nie spełnia warunek podany w instrukcji if lub w instrukcjach else if. W naszym przykładzie wywołanie „return a / b” mogłoby zostać ujęte w instrukcji else lecz nie jest to konieczne.

Należy pamiętać o kolejności poszczególnych instrukcji else if oraz else. Spójrzmy na poniższy fragment kodu:

if (warunek 1) { ... }
else if (warunek 2) { ... }
else if (warunek 3) { ... }
else { ... }

Instrukcja else, jeśli ma wystąpić to musi być ostatnią instrukcją, która zostanie spełniona gdy wszystkie wcześniejsze warunki okażą się fałszem. Dla instrukcji else nie określamy zatem już żadnych warunków.

Czy w instrukcjach warunkowych trzeba stosować nawiasy klamrowe?

Język JvaScript pozwala na pominięcie nawiasów klamrowych w instrukcjach warunkowych jeśli wykonujemy tylko jedno polecenie.

Teoretycznie zatem nasz kod można by zapisać w nieco prostszej wersji:

function Division(a,b) {
    if (b === 0) return 'Błąd - nie można dzielić przez zero!'; 
    return a / b;
}

Osobiście uważam jednak, że pominięcie nawiasów jest dopuszczalne tylko wtedy, gdy dla danego warunku przechodzimy od razu do instrukcji return, która definitywnie kończy wykonywanie danej funkcji (choć i tutaj warto stosować nawiasy). W każdej innej sytuacji uważam, że bezpieczniej jest wyrobić sobie nawyk stosowania nawiasów { … }. Uchroni nas to przed ewentualną rozbudową poleceń wykonywanych przy spełnieniu danego warunku.

Jak na prawdę działa instrukcja else if?

Konstrukcje if … else if … else stosowane są do określania większej liczby warunków związanych z daną operacją. Poniżej pokazano przykład zastosowania konstrukcji else if:

function HowManyPeople(n) {
    if (n <= 0) {return 'None people';}
    else if (n === 1) {return 'One person';}
    else if (n > 1) {return 'A lot of people';}
}

Powyższa funkcja przyjmuje jeden argument i zwraca odpowiedni komunikat w zależności od wskazanej ilości ludzi (jako parametr n). Teoretycznie powyższy zapis może wydawać się przejrzysty i dość zwięzły. Przeanalizujmy jednak poniższy kod, który pokazuje, w jaki sposób tak na prawdę interpretowane są kolejne instrukcje else if:

function HowManyPeople(n) {
    if (n <= 0) {return 'None people';}
    else {
        if (n === 1) {return 'One person';}
        else {
           if (n > 1) {return 'A lot of people';}
        }
    }
}

Analiza drugiej wersji tej samej funkcji jest z pewnością bardziej czasochłonna. W przypadku większej liczby warunków (ponad 3) lepiej rozważyć zastosowanie instrukcji switch która jest również szybsza przy dużej liczbie możliwych warunków.

Operator „trójkowy”

W programowaniu często trzeba zastosować proste warunki i na ich podstawie przypisać wartość do zmiennej czy w odpowiedni sposób wykonać instrukcję return.

Można wtedy zastosować krótszą formę instrukcji if … else w postaci tzw. operatora trójkowego. Składa się on, jak sama nazwa wskazuje, z trzech członów:

( warunek ) ? (instrukcja jeśli prawda) : (instrukcja jeśli fałsz)

Pierwszy człon to warunek, który podlega sprawdzeniu. Następnie znajduje się symbol „?”, po którym należy umieścić instrukcję do wykonania w przypadku spełnienia warunku. Dalej, po znaku „:” znajduje się instrukcja wykonywana w przypadku nie spełnienia warunku.

Warunek jak również instrukcje wykonywane dla true/false nie muszą być ujęte w nawiasach. Osobiście stosuję jednak zasadę, że zawsze warunek (czyli pierwszy człon wyrażenia trójkowego) zapisuję w nawiasach. W dalszych instrukcjach pomijam nawiasy tylko  przypadku prostych instrukcji. Przy bardziej złożonych instrukcjach wykonywanych dla true/false zalecam stosowanie nawiasów „( … )” w celu wyraźnego rozdzielenia poszczególnych elementów wyrażenia trójkowego.

W operatorze trójkowym obowiązkowo muszą wystąpić wszystkie trzy człony.

Jak stosować operator trójkowy?

Poniżej pokazano przykład funkcji, która pobiera jako argumenty dwie liczby i zwraca informację, która z przekazanych liczb jest większa:

function whichBigger(a,b) {
    var bigger = (a > b) ? a : b;
    return 'Bigger number: ' + bigger;
}

whichBigger(1,2); //Bigger number: 2
whichBigger(5,3); //Bigger number: 5

Zapis ten jest równoważny z poniższym kodem:

function whichBigger(a,b) {
    var bigger;
    if (a > b) {bigger = a;}
    else {bigger = b;}
    return 'Bigger number: ' + bigger;
}

Jak widać zapis z operatorem trójkowym jest krótszy i łatwiejszy w analizie. Mamy jednak pewien problem. Otóż co w przypadku podania dwóch takich samych liczb? Obecnie w takim wypadku również otrzymamy komunikat „Bigger number…”. Dokładniej mówiąc nastąpi przypadek niespełniania warunku (a > b), czyli do zmiennej bigger zostanie przypisana liczba „b”. Uzupełnijmy więc naszą funkcję o dodatkowy komunikat „Two same numbers” w przypadku gdy a === b.

function whichBigger(a,b) {
    if (a === b) {return 'Two same numbers';}
    else if (a > b) {return 'Bigger number: ' + a;}
    else {return 'Bigger number: ' + b;}
}

Rodzi się jednak pytanie, czy i w tym wypadku można zastosować operator trójkowy? Odpowiedź brzmi TAK, jednak jest pewne „ale”… Przeanalizujmy poniższy kod:

function whichBigger(a,b) {
    return (a === b) ? 'Two same numbers' : 'Bigger number: ' + ((a > b) ? a : b);
}

W takim zapisie łatwo o pomyłkę związaną z pominięciem niektórych nawiasów co może istotnie zmieniać sens działania polecenia return. Ponad to zagnieżdżanie operatorów trójkowych utrudnia wprowadzanie zmian – dużo łatwiej w tym wypadku analizować konstrukcje z użyciem else if lub switch.

Osobiście uważam, że operatory trójkowe są doskonałym rozwiązaniem przy prostych, pojedynczych warunkach. Jeśli musimy użyć dodatkowych wariantów zalecam rozważenie, czy lepszym rozwiązaniem nie będzie w tym wypadku konstrukcja else if lub switch. Czytelność kodu powinna być w tym momencie ważniejsza niż zaoszczędzenie kilku dodatkowych linijek kodu.