Wyrażenia logiczne w JavaScript
Cześć! To jest fragment książki JavaScript od podstaw, która ma pomóc w nauce programowania od zera.
Do tej pory widzieliśmy zaledwie bardzo proste warunki sprawdzające wartość jednej zmiennej. Jak zachować się w sytuacji, gdy chcielibyśmy wykonać akcję, w której jednocześnie dwa warunki są spełnione? Na przykład:
- czy tweet jest promowany i pochodzi od osoby obserwowanej,
- czy tweet jest własny lub pochodzi od osoby obserwowanej.
Aby uzyskiwać odpowiedzi na takie pytania potrzebujemy operatorów logicznych, czyli stawianych między wartościami logicznymi.
Operator i &&
Aby dobrze zrozumieć operatory logiczne, przytoczę historię małego Jasia. Chciał on pograć na komputerze, więc poszedł do mamy poprosić o pozwolenie. Ta jednak powiedziała: "Będziesz mógł pograć, gdy odrobisz lekcje i posprzątasz pokój".
Ten warunek w programowaniu mógłby być wyrażony następująco:
const canPlayGames = finishedHomework && cleanedRoom;
Widzimy tam zmienne finishedHomework
i cleanedRoom
, reprezentujące kolejno ukończenie zadań domowych i posprzątanie pokoju (zakładamy, że są to wartości logiczne, czyli true
lub false
). Pomiędzy nimi widzimy natomiast operator i &&
1, który sprawia, że całe wyrażenie jest true
tylko wtedy, gdy obydwie jego strony są true
2.
a | b | a && b |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
console.log(true && true); // true
console.log(true && false); // false
console.log(false && true); // false
console.log(false && false); // false
// A czy Jasio może mieć szczeniaka?
const haveTime = true;
const isResponsible = false;
const canHavePuppy = isResponsible && haveTime;
console.log(canHavePuppy); // false
Za przykładowe częste wykorzystanie tego operatora uznaje się sprawdzenie, czy liczba znajduje się w jakimś zakresie: czy jest większa od minimum i mniejsza niż maksimum. Dla przykładu moglibyśmy sprawdzić, czy zmienna percent
ma poprawną wartość, czyli czy znajduje się między 0 a 100.
if (percent >= 0 && percent <= 100) {
console.log("Poprawna wartość");
}
Operator lub ||
Niestety Jasio nie odrobił lekcji, zatem nie mógł pograć na komputerze. Wtedy jednak zadzwonili do niego znajomi i zaprosili go do kina. Jasio poszedł do mamy i poprosił o pieniądze na wyjście, a ta powiedziała: "Dostaniesz pieniądze, jeśli posprzątasz garaż lub umyjesz samochód". Ten warunek można przedstawić następująco:
const willGoToCinema = cleanGarage || washedCar;
Operator ||
3 reprezentuje alternatywę. Całe wyrażenie zwraca true
, gdy spełnione jest albo cleanGarage
, albo washedCar
, albo oba te warunki jednocześnie.
a | b | a || b |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
console.log(true || true); // true
console.log(true || false); // true
console.log(false || true); // true
console.log(false || false); // false
// Czy Jasio dostanie czekoladę?
const behavedWell = true;
const cleanedRoom = false;
const canEatChocolate = behavedWell || cleanedRoom;
console.log(canEatChocolate); // true
Jasio tak się zapalił do pracy, że zarówno posprzątał garaż, jak i umył samochód. Zdziwiona mama powiedziała: "Nie chciałam byś robił i jedno i drugie", a Jasio odpowiedział: "Użyłaś łącznika lub, który akceptuje zrobienie zarówno jednego, jak i drugiego, podobnie jak ||
w programowaniu". Mama Jasia zaakceptowała to wyjaśnienie, więc poszedł on z kolegami do kina.
Operator negacji !
Ostatnim operatorem logicznym, który powinniśmy poznać jest zaprzeczenie, czyli pojedynczy wykrzyknik !
przed wartością logiczną. Podobnie jak -
(minus) przed liczbą zamienia jej znak na odwrotny, tak !
(operator negacji) przed wartością logiczną zamienia ją na odwrotną. Operator negacji !
zamienia true
na false
, a false
na true
. Dlatego często tłumaczy się go jako "nie".
cond | !cond |
---|---|
true | false |
false | true |
console.log(!true); // false
console.log(!false); // true
const isAmazing = true;
console.log(!isAmazing); // false
const isBoring = false;
console.log(!isBoring); // true
// Podobnie jak przy liczbach
const positive = 1;
console.log(-positive); // -1
const negative = -1;
console.log(-negative); // 1
Programowanie akceptuje podwójne zaprzeczenie, co oznacza, że każdy kolejny znak zaprzeczenia odwraca wartość logiczną4.
console.log(true); // true
console.log(!true); // false
console.log(!!true); // true
console.log(!!!true); // false
console.log(!!!!true); // true
Operator negacji !
stawia się często w bardziej złożonym wyrażeniu przed pojedynczą zmienną. Jasio zrozumiał to wtedy, gdy jego mama powiedziała: "Będziesz mógł grać na komputerze, jeśli nie zawalisz sprawdzianu z matematyki i posprzątasz pokój". Wykorzystał on zaprzeczenie, by przełożyć to zdanie na język programistyczny.
const canPlayGames = !failedMathTest && cleanedRoom;
Zwróć uwagę, że negacja odnosi się tutaj tylko do niezawalenia sprawdzianu. !true && false
zwraca false
, podobnie jak -10 + 11
równe jest 1
, a nie -21
. Jeśli chcielibyśmy, aby zaprzeczenie negowało większy obszar wyrażenia, powinniśmy cały ten fragment otoczyć nawiasami. !(true && false)
zwraca true
.
// Sprawdzian zaliczony
const failedMathTest = false;
// Pokój nieposprzątany
const cleanedRoom = false;
// Nie zawaliłeś sprawdzianu i posprzątałeś pokój
console.log(!failedMathTest && cleanedRoom); // false
// Nie jest tak, że
// zawaliłeś sprawdzian i posprzątałeś pokój
console.log(!(failedMathTest && cleanedRoom)); // true
Czytanie wyrażeń logicznych
Warto poćwiczyć czytanie wyrażeń logicznych na głos. Ja zwykle robię to następująco:
&&
czytam jako i.||
czytam jako lub.!
przed zmienną czytam jako nie, a przed nawiasem jako nie jest tak, że.
Poćwiczmy to na poniższych przykładach, dla zmiennych:
isGrounded
jako "ma szlaban"cleanedRoom
jako "posprzątał pokój"passedTest
jako "zaliczył sprawdzian"
Przeczytaj poniższe zdania:
cleanedRoom && passedTest
- posprzątał pokój i zaliczył sprawdzian.!isGrounded || passedTest
- nie ma szlabanu lub zaliczył sprawdzian.cleanedRoom && !passedTest
- posprzątał pokój i nie zaliczył sprawdzianu.!(isGrounded || !passedTest)
- nie jest tak, że ma szlaban lub nie zaliczył sprawdzianu.!(!cleanedRoom && !passedTest)
- nie jest tak, że nie posprzątał pokoju i nie zaliczył sprawdzianu. Jest to równoznaczne zcleanedRoom || passedTest
.
a || b
jest zawsze równoznaczne z!(!a && !b)
, aa && b
jest równoznaczne z!(!a || !b)
. Takie obrócenia stosuje się, aby pozbyć się ciężkiego do przeczytania zaprzeczenia. Przykładowo, jeśli mielibyśmy warunek!(hasComputer && !isGrounded)
to moglibyśmy to zamienić na!hasComputer || isGrounded
. Wykrzyknik przyisGrounded
zniknął, bo!!a
jest równoznaczne za
.
Ćwiczenie: Złożone wyrażenia logiczne
Określ, co wypisze do konsoli poniższy kod:
const hasComputer = true;
const passedTest = false;
let isGrounded = false;
console.log(hasComputer && passedTest);
console.log(passedTest || isGrounded);
console.log(hasComputer && !passedTest);
let canPlayGames = hasComputer && !isGrounded;
console.log(canPlayGames);
let playGames = hasComputer && canPlayGames;
if (!passedTest) {
isGrounded = true;
}
console.log(playGames);
console.log(passedTest || !isGrounded);
console.log(!(!hasComputer || !passedTest));
Odpowiedzi na końcu książki.
Wartości falsy i truthy
JavaScript posiada specjalną funkcjonalność sprawiającą, że jeśli w warunku umieścimy wartość, która nie jest wartością logiczną (true
albo false
), to mimo wszystko zostanie zinterpretowana jako wartość logiczna. W JavaScript używa się jej powszechnie najczęściej jako alternatywy dla sprawdzenia, czy obiekt nie jest null
albo undefined
. Dzieje się tak dlatego, że obydwie wartości są traktowane jako false
(mówi się na nie falsy) w czasie, gdy obiekty są traktowane jako true
(są truthy). W przypadku gdy wiemy, że zmienna zawiera obiekt, null
albo undefined
, użycie jej w warunku if można traktować jak sprawdzenie, czy dana wartość jest obiektem.
if (user) {
// Robimy coś z user
}
// Ekwiwalent, zakładając że user to obiekt
if (user !== null && user !== undefined) {
// Robimy coś z user
}
W całym JavaScript falsy (a więc interpretowane jako false
) są następujące wartości5:
false
,null
,undefined
,0
(oraz-0
i0n
),NaN
,""
.
Wszystkie inne wartości interpretowane są jako true
i mówimy o nich, że są truthy.
Zwróć uwagę na pułapkę powyższego sprawdzenia dla tekstów i liczb, ponieważ zostaną one odrzucone także w przypadku ""
(pustego stringa) albo 0
.
Zwracanie wartości przez &&
i ||
Uwaga: Ta sekcja jest bardziej zaawansowana i nie będzie potrzebna w dalszej części książki. Jeśli nie czujesz się pewnie z dotychczasową wiedzą, pomiń ją.
W JavaScript nie tylko wartości logiczne mogą być użyte jako warunek w instrukcji if. Podobnie operatory logiczne &&
i ||
akceptują inne wartości, niż tylko true
i false
. W takiej sytuacji wartość zwracana przez te operatory może nie być wartością logiczną.
Operator &&
zwraca lewą stronę, jeśli była falsy lub prawą, jeśli lewa była truthy.
console.log(true && true) // true
console.log(true && false) // false
console.log(false && true) // false
console.log(false && false) // false
console.log(true && "Prawa") // Prawa
console.log(false && "Prawa") // false
console.log(true && null) // null
console.log("Lewa" && "Prawa") // Prawa
Operator ||
zwraca lewą stronę, jeśli była truthy lub prawą, jeśli lewa była falsy.
console.log(true || true) // true
console.log(true || false) // true
console.log(false || true) // true
console.log(false || false) // false
console.log(true || "Prawa") // true
console.log(false || "Prawa") // "Prawa"
console.log("Lewa" || "Prawa") // "Lewa"
Ta cecha języka JavaScript jest często wykorzystywana jako krótsza alternatywa dla operatora warunkowego.
Ćwiczenie: Wartości zwracane przez operator warunkowy i operatory logiczne
- Dla jakiej wartości
name
poniższy fragment kodu wyświetli "Ma na imię Jasio", a dla jakich tylko pustego stringa?
const nameLabel = name ? "Ma na imię " + name : ""
console.log(nameLabel)
- Dla jakiej wartości
age
poniższy fragment kodu wyświetli "Ma 10 lat", a dla jakichnull
?
const nameLabel = age ? "Ma " + age + " lat" : null
console.log(nameLabel)
- Dla jakiej wartości
name
poniższy fragment kodu wyświetli "Ma na imię Janek"? Co zostanie wyświetlone dlaname
będącego falsy?
const nameLabel = name && "Ma na imię " + name
console.log(nameLabel)
- Dla jakiej wartości
age
poniższy fragment kodu wyświetli wartośćage
, a dla jakich 0?
const displayAge =
(age && typeof age === "number" && age > 0) ? age : 0
console.log(displayAge)
Odpowiedzi na końcu książki.
&&
to podwójny znak et (ang. ampersand), po angielsku oznaczający "and", czyli "i".
Precyzyjniej mówiąc, całe wyrażenie jest truthy, gdy obydwie strony są truthy.
||
to podwójna kreska pionowa, określana czasem jako pipe. W programowaniu często jest utożsamiana z alternatywą.
Bawiąc się językiem, możemy powiedzieć: nie jest tak, że programowanie nie akceptuje podwójnego zaprzeczenia. A nawet (nie nie nie nie) akceptuje wielokrotne zaprzeczenie.
Umieściłem pełną listę, ale wartości NaN
i 0n
nie były omawiane i nie będą nam potrzebne w dalszej części książki. Dla zainteresowanych NaN
to skrót od Not-A-Number i powstaje, gdy na przykład próbujemy odczytać liczbę z czegoś, co liczbą nie jest parseInt("whatever")
. Jeśli chodzi o n
po liczbie, to jest wykorzystywane, by przechowywać potencjalnie bardzo duże liczby (typ "bigint"
).