Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!
⛔ Potrzebujesz wsparcia? Oceny CV? A może Code Review? ✅ Dołącz do Discorda!
Dzięki zrozumieniu operatorów logicznych dużo łatwiej będzie Ci debugować swoje rozwiązania. Gdy nauczysz się przenosić wyrażenia logiczne do funkcji, Twój kod zyska na czytelności. Ten artykuł pomoże Ci usystematyzować wiedzę z zakresu łączenia operatorów logicznych w JS i poznać przypadki ich zastosowania.
Jest to kontynuacja tematu operatorów logicznych w JavaScripcie. Jeśli nie znasz jeszcze podstaw, zapraszam Cię do lektury poprzedniego artykułu: „Operatory logiczne w JavaScript – podstawy”.
W praktyce nie wpisuje się wartości do wyrażeń bezpośrednio (np. "nice day" && "cheerful people"). Przechowuje się je w zmiennych (czasem też zwraca bezpośrednio z funkcji, ale o tym za chwilę). Zasada działania operatorów logicznych się nie zmienia – w miejsce zmiennych interpreter JavaScript podstawi sobie przechowywane przez nie wartości.
Kilka przykładów wystarczy dla zrozumienia tematu:
// operator AND const user = "regular" const isLoggedIn = true console.log(user === "regular" && isLoggedIn) // true, ponieważ operator porównania zwraca true i zmienna isLoggedIn przechowuje true // operator OR let isGameEnd = false let score = 0 console.log(isGameEnd || score > 0) // false, ponieważ zmienna isGameEnd przechowuje false i opertor porównania zwraca false // operator NOT const student = {name: "Lizzy", age: 21} console.log(!student) // false -> zanegowaliśmy wartość prawdziwą (obiekt to wartość truthy), więc otrzymujemy false // dla utrwalenia jeszcze podwójna negacja: const animal = "cat" console.log(!!animal) // true
W większości przypadków wartości będą trafiać do naszych zmiennych dynamicznie – czyli z funkcji, których wynik działania zależy od jakichś zewnętrznych czynników, np. od tego:
Te wszystkie operacje obsługiwane są przez funkcje. Dla przykładu stwórzmy sobie takie uproszczone funkcje, które weryfikują wartości od użytkownika. Sprawdzimy, czy użytkownik znajdzie największą liczbę oraz literę, która występuje w alfabecie wcześniej od innych:
// wyświetlam użytkownikowi okienko z inputem i wprowadzoną przez niego wartość zapisuję od razu w zmiennej: const userNumber = prompt('Która liczba jest największa? -> 3, 10, 5'); // to samo robię z drugim zadaniem const userLetter = prompt( 'Która litera alfabetu występuje wcześniej niż inne? -> r, k, f' ); function isBiggest(number) { return number === '10'; // wartość z okienka prompt zawsze jest stringiem, więc porównam ją do stringa } function isCorrectLetter(letter) { return letter === 'f'; } // zapisuję wynik działania funkcji do zmiennych const isNumberGuessed = isBiggest(userNumber); const isLetterGuessed = isCorrectLetter(userLetter); // aby użytkownik zwyciężył, obie wartości zwracane z funkcji muszą być true – wówczas zmienna isWinner będzie przechowywać true // jeśli zmienna isWinner będzie przechowywać false, użytkownik przegra const isWinner = isNumberGuessed && isLetterGuessed; console.log(isWinner);
W powyższym przykładzie w wyrażeniu logicznym umieściliśmy zmienne przechowujące wynik działania funkcji. Interpreter JS wstawił sobie w to miejsce wartości zwracane z funkcji:
// jeśli użytkownik wprowadzi poprawne wartości, interpreter JS odczyta to tak: const isWinner = true && true // co da w efekcie true // jeśli użytkownik wprowadzi np. niepoprawną liczbę, interpreter JS odczyta to tak: const isWinner = false && true // co da w efekcie false
Kod sprawdzający zwycięstwo zadziałałby dokładnie tak samo, gdybyśmy do wyrażenia logicznego wstawili wywołanie funkcji:
const isWinner = isBiggest(userNumber) && isCorrectLetter(userLetter) console.log(isWinner)
Czasem jednak warto zadbać o czytelność kodu i wybrać rozwiązanie zastosowane przez nas wcześniej.
Jeżeli temat funkcji jest Ci jeszcze nieznany, polecam artykuł dla początkujących: „Deklaracja i wywołanie funkcji w JavaScript”.
Operatory logiczne można łączyć! Należy brać pod uwagę, że:
Przykładowo poniższe wyrażenie zwróci wartość truthy:
console.log( "nice day" && "coffee" || null ) // "coffee"
Odczytywane jest bowiem po kolei: "nice day" && "coffee" daje wartość truthy (zwracany jest string "coffee") i gdy dochodzimy do operatora || kończymy sprawdzanie. Pamiętasz zasadę dla OR? „Po co sprawdzać resztę, skoro już wystąpiła wartość prawdziwa”.
Skomplikujmy:
console.log( "coffee" && null || 2 && 3 ) // 3
Pamiętajmy: AND ma wyższy priorytet niż OR, więc interpreter nasze wyrażenia pogrupuje (sami też możemy to zrobić w kodzie):
( "coffee" && null ) || ( 2 && 3)
Pierwsza operacja zwróci null, a więc wartość fałszywą. Druga operacja zwróci 3, a więc wartość prawdziwą. Ostateczna operacja to:
null || 3
Tu już widać, że otrzymamy 3.
Został jeszcze operator NOT. Wplećmy go w poprzedni przykład:
console.log( "coffee" && !null || 2 && 3 ) // true
Sprawdzamy od lewej: "coffee" to wartość prawdziwa, !null zwraca true, więc to też wartość prawdziwa. Ostateczny wynik dla "coffee" && !null to właśnie true (pamiętajmy, że operator AND zwraca ostatnią wartość, jeśli wszystkie były prawdziwe). I na tym kończymy, ponieważ napotykamy operator ||, a w jego przypadku „po co sprawdzać resztę, skoro już wystąpiła wartość prawdziwa”.
Instrukcja warunkowa to chyba najczęstsze miejsce wykorzystania operatorów logicznych. Weźmy nasz przykład ze zgadywaniem liczby i litery. Moglibyśmy wyświetlać komunikat o wygranej lub przegranej:
const userNumber = prompt('Która liczba jest największa? -> 3, 10, 5'); const userLetter = prompt( 'Która litera alfabetu występuje wcześniej niż inne? -> r, k, f' ); function isBiggest(number) { return number === '10'; // wartość z okienka prompt zawsze jest stringiem, więc porównam ją do stringa } function isCorrectLetter(letter) { return letter === 'f'; } const isNumberGuessed = isBiggest(userNumber); const isLetterGuessed = isCorrectLetter(userLetter); const isWinner = isNumberGuessed && isLetterGuessed; // instrukcja warunkowa korzystająca z wyrażenia logicznego zamieszczonego w zmiennej isWinner if (isWinner) { alert('Gratulacje, wygrałeś!'); } else { alert('Odpowiedzi nieprawidłowe. Spróbuj ponownie'); }
Jeżeli wyrażenie logiczne jest krótkie jak w tym przypadku, możemy pominąć etap tworzenia zmiennej isWinner i od razu wpisać wyrażenie do warunku:
const isNumberGuessed = isBiggest(userNumber); const isLetterGuessed = isCorrectLetter(userLetter); // instrukcja warunkowa korzystająca z wyrażenia logicznego zamieszczonego w zmiennej isWinner if (isNumberGuessed && isLetterGuessed) { alert('Gratulacje, wygrałeś!'); } else { alert('Odpowiedzi nieprawidłowe. Spróbuj ponownie'); }
Pamiętaj, że jeśli wyrażenie logiczne jest długie i łatwo się w nim zgubić, to warto przenieść je do funkcji i to ją wstawić w miejsce wyrażenia. Na początku nauki, zanim poznasz sposoby optymalizacji różnych rozwiązań (np. obsługi błędów w formularzu JS), będziesz pewnie tworzyć takie konstrukcje:
if ( nameValue.length > 0 && surnameValue.length > 0 && emailValue.contains('@') && passwordValue.length >= 6 ) { console.log('The registration was successful'); } else { console.log('Try again'); }
Wówczas przy refaktoryzacji warto przenieść warunek do funkcji, np. tak:
// warunek czytamy: jeżeli formularz jest wypełniony prawidłowo, to… if (isFormFilledCorrectly()) { console.log('The registration was successful'); // w przeciwnym razie… } else { console.log('Try again'); } // wyrażenie logiczne przeniesione do funkcji function isFormFilledCorrectly() { return ( nameValue.length > 0 && surnameValue.length > 0 && emailValue.contains('@') && passwordValue.length >= 6 ); }
Zwróć ponownie uwagę, że należy pamiętać o słowie return – w przeciwnym razie ciągle będziemy otrzymywać wartość falsy (undefined).
Jeżeli chcielibyśmy wykonać jakieś akcję zależną tylko od tego, że pola nie są wypełnione nieprawidłowo, możemy użyć operatora NOT:
// warunek czytamy: jeżeli formularz NIE jest wypełniony prawidłowo, to… if (!isFormFilledCorrectly()) { alert('The form contains invalid values and cannot be submitted.'); }
To, CO zwraca operator (a nie JAKIE wartości: truthy czy falsy) jest nieraz wykorzystywane do dynamicznego ustawiania wartości zmiennych. Oznacza to, że nie musimy tworzyć instrukcji warunkowej, która nam te wartości ustawi. Stanie się to „samo” dzięki logice działania operatorów.
Spójrz na poniższy przykład:
const user = logUser() // spodziewamy się otrzymania z funkcji obiektu z danymi użytkownika const userName = user && user.name
W przykładzie zmienna user będzie przechowywać obiekt z danymi użytkownika tylko wówczas, gdy logowanie się powiedzie, w przeciwnym razie zmienna będzie mieć wartość falsy (np. autor kodu zwraca null).
Dzięki temu ustawimy nazwę użytkownika tylko wówczas, gdy będziemy mieć już dostęp do obiektu. Jeśli zmienna user będzie mieć wartość falsy, to dalsza część kodu się nie wykona – w ten sposób zabezpieczamy się przed błędem. Gdybyśmy tego nie zrobili, otrzymalibyśmy komunikat, że próbujemy odczytać właściwość .name na obiekcie, którego nie ma:
Uncaught TypeError: Cannot read properties of null (reading 'name')
Wyobraźmy sobie, że mamy projekt, w którym style zgromadziliśmy w osobnym pliku jako motyw. W przykładzie wybrany kolor umieściliśmy wcześniej w zmiennej themeColor i teraz chcemy ustawić go dla tekstu elementu articleElement.
const articleElement.style.color = themeColor || "black"
W przykładzie, jeśli nie będziemy mieć danych o kolorze z motywu w zmiennej themeColor (np. zabraknie odpowiedniego pliku i zmienna themeColor będzie przechowywać wartość falsy), to ustawi się kolor czarny. Służy to jako backup.
Pusty obiekt {} i pusta tablica [] to nie wartość fałszywa. Jeśli więc np. pobierasz dane przez API i chcesz powstrzymać operacje na danych, jeśli otrzymasz pusty obiekt, to trzeba rozwiązać to inaczej, np. sprawdzić, czy obiekt ma klucze. Jeśli jest opcja otrzymania pustej tablicy, możesz sprawdzić, czy jej długość jest większa od 0.
Pamiętaj, że decyduje kolejność sprawdzania warunków (od lewej do prawej) oraz że mają one priorytety. Jeżeli więc np. uzależniasz uruchomienie funkcji od innej zmiennej, to funkcja ta może się nigdy nie wykonać:
const user = isLoggedIn && showNewsletterBox()
Funkcja showNewsletterBox() nie zostanie uruchomiona, jeżeli zmienna isLoggedIn przechowuje wartość falsy. Świadomość tego pomoże Ci przy debugowaniu – zamiast szukać przyczyny w showNewsletterBox() (w końcu nie widzimy efektów jej działania, więc „to pewnie z nią jest coś nie tak”), warto sprawdzić, czy przypadkiem z jakiegoś powodu zmienna isLoggedIn nie ma ciągle przypisywanej wartości fałszywej.
Jeżeli wyrażenie logiczne jest długie i nie jesteś już pewien, co zwrócą konkretne fragmenty, a priorytety Ci się mieszają, użyj nawiasów okrągłych:
playerType === "begginer" && isLoggedIn() || isSessionContinued() || !isGameEnd && score !== 0 // pogrupowany kod: ( playerType === "begginer" && isLoggedIn() ) || isSessionContinued() || ( !isGameEnd && score !== 0 )
W takim rozbudowanym przypadku lepiej byłoby jednak pomyśleć o przeniesieniu wyrażenia logicznego do osobnej funkcji.
Staraj się nie tworzyć długich wyrażeń logicznych, ale jeżeli już je masz i widzisz, że kod z instrukcji warunkowej się nie wykonuje, to w debugowaniu pomoże Ci np.:
Warto też wziąć kartkę i długopis i rozrysować sobie wyrażenia logiczne. Zastanów się, co zwróci każdy z operatorów, jaki ma priorytet i jak zostanie zinterpretowane to od lewej.
Staraj się unikać nazw typu isNotLoaded, ponieważ później użycie tego w instrukcji z operatorem NOT komplikuje sprawę:
if ( !isNotLoaded ) { console.log("Operation…?") }
I co teraz? Czy jeśli isNotLoaded przechowuje true, to znaczy, że dane nie zostały załadowane? Skoro teraz temu zaprzeczymy operatorem !, to w warunku będzie false i instrukcja się nie wykona. Chyba – bo dla zmęczonej kodowaniem głowy nic już nie będzie oczywiste.
Nie warto więc komplikować sobie rozwiązań. W powyższym przypadku nazwanie zmiennej isLoaded rozwiązałoby problem.
Rozumienie operatorów logicznych przyjdzie wraz z praktyką. Warto jednak wracać do tego artykułu, gdy napotkasz problemy. Na początku nauki mogą jeszcze mylić Ci się priorytety operatorów czy sposób interpretacji wyrażeń logicznych. W razie czego zawsze miej pod ręką console.log() i funkcję Boolean().
Udostępnij ten artykuł:
Potrzebujesz cotygodniowej dawki motywacji?
Zapisz się i zgarnij za darmo e-book o wartości 39 zł!
PS. Zazwyczaj rozsyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu email.
Chcesz zostać (lepszym) programistą i lepiej zarabiać?
🚀 Porozmawiajmy o nauce programowania, poszukiwaniu pracy, o rozwoju kariery lub przyszłości branży IT!
Umów się na ✅ bezpłatną i niezobowiązującą rozmowę ze mną.
Chętnie porozmawiam o Twojej przyszłości i pomogę Ci osiągnąć Twoje cele! 🎯