Operatory porównania

Ten dział poświęcony jest operatorom porównania. Operatory porównania, po porównaniu wartości zawsze zwracają nam wartości boolean. W języku JavaScript mamy w miarę standardowe operatory porównania:

== Equality operator
!= Inequality operator
=== Identity operator
!== Nonidentity operator
> Greater than operator
< Less than operator
>= Greater than or equal operator
<= Less than or equal operator

Większość tych operatorów zobaczycie także w innych językach programowania. W większości języków mogą działać podobnie, ale mamy do czynienia z JavaScript i zawsze czają się jakieś niuanse. Na szczególną uwagę na pewno zasługuje podwójny operator porównania i nierówności.

Przeanalizujemy więc sobie wszystkie operatory oraz te niuanse.

Operatory równości i nierówności == i !=

O operatorze równości, który jest reprezentowany przez dwa znaki, mówiliśmy już kilka razy. Porównuje on wartości, dokonując niejawnej konwersji:

console.log(1 == 1); // true
console.log("1" == 1) // true
console.log(true == 1) // true
console.log(null == undefined) // true

Wyniki tych porównań nie są dla nas oczywiste ani intuicyjne. Bez wspomagania się specyfikacją czy specjalną tabelką, która została stworzona przez użytkownika Dorey na Githubie ciężko zorientować się, jaki wynik otrzymamy https://dorey.github.io/JavaScript-Equality-Table/

To samo dotyczy sprawdzenia nierówności za pomocą operatora dwuznakowego !=:

console.log(1 != 1); // false
console.log("1" != 1) // false
console.log(true != 1) // false
console.log(null != undefined) // false

Wyniki ze sprawdzenia nierówności w taki sposób również nie są oczywiste i logiczne. Tu również zachodzą niejawne konwersje do różnych typów i dopiero potem następuje porównanie.

Dobra rada dla programistów JavaScript to unikanie operatorów dwuznakowych do porównywania i sprawdzania nierówności.

Operatory równości i nierówności === i !==

Operator równości i nierówności z trzema znakami to dzisiaj standard porównania dwóch wartości w JavaScript. Działa on bardzo dokładnie i transparentnie.

console.log(1 === 1); // true
console.log("1" === 1); // false
console.log(true === 1) // false
console.log(null === undefined); // false

Jego zadaniem jest sprawdzenie, czy wartości mają ten sam typ, jeżeli tak to następuje porównanie wartości i zwrócenie true lub false. Jeżeli typy są różne, od razu dostajemy wynik false bez sprawdzania już wartości.

Również operator nierówności działa tak samo:

console.log(1 !== 1); // false
console.log("1" !== 1); // true
console.log(true !== 1) // true
console.log(null !== undefined); // true

Najpierw sprawdza, czy próbujemy porównać te same typy. Jeżeli typy są różne, to zwracane jest true ponieważ już wiadomo, że różne typy mają różne wartości. Jeżeli typy są takie same, porównuje jeszcze wartości.

Operator równości i nierówności reprezentowane przez trzy znaki to operatory, które na co dzień będziecie używać w JavaScript. Działanie tych operatorów jest proste i intuicyjne, bo nie zachodzą tutaj żadne konwersje.

Operator większy > i mniejszy <

Operator większości w JavaScript działa tak samo, jak w matematyce:

console.log(100 > 1); // true
console.log(1 > 100); // false
console.log(1 > 1); // false

Jeżeli lewa strona jest większa otrzymujemy wartość true w innym wypadku wartość false.

Operator mniejszości działa w drugą stronę sprawdzając, czy lewa strona jest mniejsza:

console.log(100 < 1); // false
console.log(1 < 100); // true
console.log(1 < 1); // false

Wszystko jest bardzo proste i oczywiste, gdy pracujemy na liczbach. Zobaczmy co się dzieje, gdy używamy różnych typów:

console.log('100' > 1); // true
console.log(true > false); // true
console.log(false < 1); // true

Jeżeli używamy różnych typów, JavaScript próbuje wartości sprowadzić do typu number . Dlatego udaje nam się porównać liczbę '100' zapisaną jako string z liczbą 1. Nastąpi konwersja string do number, a potem nastąpi porównanie.

Również udaje nam się porównać wartości true i false ponieważ to są wartości, które konwertują się na typ number.

Zobaczmy jednak ciekawsze przykłady:

console.log(1 > 'foo'); // false

W tym przykładzie próbujemy sprawdzić, czy 1 jest większe od losowej wartości tekstowej. String, który nie reprezentuje liczby w żaden sposób, zostanie przekonwertowany na wartość NaN.

Wartość NaN ani nie jest większa od liczby, ani nie jest mniejsza od żadnej liczby:

console.log(0 > NaN); // false
console.log(0 < NaN); // false

Nie da się porównać wartości NaN z liczbami, pomimo że jest typu number. Każde porównanie zwróci false.

Samo undefined konwertowane do liczby także zwraca NaN.

console.log(0 > undefined); // false
console.log(0 < undefined); // false

Porównanie undefined z jakąś wartością liczbową zawsze będzie zwracała wartość false w każdym wariancie.

Inaczej jest z wartością null:

console.log(1 > null); // true
console.log(1 < null); // false

Wartość null konwertowana do number zwraca wartość 0 dlatego jest możliwe porównanie z liczbą. W przypadku sprawdzenia, czy 1 jest większe od null otrzymamy wartość true.

Tak więc gdy używamy dwóch różnych typów, następuje próba sprowadzenia ich do typu number i trzeba mieć to na uwadze.

Porównanie stringów

Inaczej jest, gdy próbujemy porównać dwie wartości o typie string, nie zachodzi tutaj żadna konwersja do typu number, a porównanie ciągów tekstowych:

console.log('a' > 'A'); // true
console.log('AB' > 'a') // false
console.log('a' > '1234') // true

Przy takim porównaniu pod uwagę brany jest każdy znak, a nie długość ciągów tekstowy. Porównany jest nie tyle znak co jego wartość w tablicy Unicode.

W pierwszym przypadku literka a ma numer kodowy 97 natomiast duża litera A ma numer kodowy 65, dlatego wbrew intuicji otrzymujemy wartość true. Czyli mała literka jest większa od dużej.

W drugim przypadku gdzie porównujemy ciąg z dwóch znaków, do ciągu z jednym znakiem okazuje się, że ciąg znaków AB
wcale nie jest większy niż jedna litera a. Duża litera A ma mniejszy numer kodowy niż mała litera a dlatego od razu zwracany jest wynik false, a dalsze porównanie nie następuje.

Takich porównań na wartościach string raczej nie będziecie używać lub nie powinniście. Przy porównaniach większe i mniejsze najbezpieczniejszą opcją jest praca na wartościach typu number.

Operator większy i równy >= oraz mniejszy i równy <=

Operatory większy i równy oraz mniejszy i równy podlegają tym samym zasadom co operator większy i mniejszy.

Oprócz tego, że dodatkowo sprawdzają możliwą równość.

console.log(100 <= 1); // false
console.log(1 >= 100); // false
console.log('100' >= 1); // true
console.log(1 <= '100'); // true
console.log(1 >= 'blabla'); // false
console.log(1 >= null); // true
console.log(1 <= null); // false

Jeżeli jakaś strona jest inna niż typ number następuje konwersja.

Jeżeli są to stringi, następuje porównanie ciągów znakowych według tabeli Unicode i tych samych zasad co omówiliśmy.

console.log('AB' >= 'a') // false
console.log('a' <= '1234') // false

Pomimo tego, że przy operatorach większy i mniejszy, także następują niejawne konwersje, nie są one tak straszne jak przy podwójnym operatorze sprawdzania równości. Warto jednak te operatory pozostawić do pracy z liczbami, ponieważ praca na typie string nie jest zbyt czytelna i intuicyjna.

Co warto zapamiętać

  • dwuznakowy operator równości dokonuje niejawnych konwersji przed porównaniem i jego wyniki nie są oczywiste
  • operator porównania z trzema znakami sprawdza typ i wartość, jest to bezpieczna i najlepsza forma porównania
  • operatory większości i mniejszości pracują na typie number jeżeli nie dostarczamy typu number następuje konwersja do takiego typu
  • jeżeli do operatorów większości i mniejszości podstawimy stringi nastąpi próba porównywania stringów według kodów Unicode