Dwa sposoby, by zapytać „czy to jest równe?”
JavaScript daje ci dwa operatory równości: === (ścisły) i == (luźny). Wyglądają niemal identycznie. Zachowują się zupełnie inaczej.
=== sprawdza, czy oba operandy mają ten sam typ i tę samą wartość. == najpierw konwertuje operandy do wspólnego typu, a dopiero potem porównuje. Ta konwersja, czyli koercja typów, stoi za większością złej sławy porównań w JavaScript.
Wersja skrócona: domyślnie używaj ===. Wersję długą warto przeczytać raz, żeby wiedzieć, z czego rezygnujesz.
Równość ścisła: to, czego prawdopodobnie chcesz
=== zwraca true tylko wtedy, gdy obie strony zgadzają się typem i wartością. Bez koercji, bez niespodzianek:
Jeśli typy się nie zgadzają, odpowiedź to od razu false. Jeśli się zgadzają, JavaScript porównuje wartości. Dla typów prostych to porównanie wartości, dla obiektów porównanie referencji (więcej o tym za chwilę).
!== to operator ścisłej nierówności i działa według tych samych zasad, tylko odwrotnie.
Równość luźna: koercja w przebraniu
== pozwala, by obie strony miały różne typy. Przed porównaniem odprawia taniec koercji, żeby je do siebie dopasować:
Dokładne zasady są w specyfikacji i nie są straszne, ale przypominanie ich sobie w trakcie debugowania to inna sprawa. To, że "0" == false daje true, zaskakuje nawet doświadczonych programistów. Podobnie [] == false (też true, bo tablica zamienia się w "", a to w 0).
Dlatego większość przewodników stylu, a także reguła eqeqeq w ESLint, każe domyślnie używać ===. Płacisz jednym znakiem więcej, a dostajesz zasady, które naprawdę da się zapamiętać.
Jedyny przydatny wzorzec z ==
Jest jeden idiom z luźną równością, który warto znać: x == null zwraca true, gdy x to null albo undefined, i false dla wszystkiego innego.
Ścisły odpowiednik to x === null || x === undefined, który działa, ale czyta się go z większym szumem. Wiele projektów dopuszcza == null jako jedyny uświęcony wyjątek. Wybierz regułę i stosuj ją konsekwentnie.
Obiekty porównuje się po referencji
W przypadku obiektów, tablic i funkcji zarówno ===, jak i == zadają to samo pytanie: „czy obie strony wskazują na ten sam obiekt w pamięci?”. A nie: „czy mają tę samą zawartość?”.
Dwa literały obiektów o identycznej zawartości to nadal dwa różne obiekty. Każdy przynajmniej raz się na tym potknie.
Żeby porównać po wartości, piszesz własną funkcję pomocniczą, używasz biblioteki (lodash.isequal) albo w przypadku prostych obiektów serializujesz je przez JSON.stringify:
JSON.stringify działa tylko dla prostych danych: pomija funkcje, undefined i symbole, a przy niektórych kształtach nie gwarantuje tej samej kolejności kluczy we wszystkich silnikach. To szybkie sprawdzenie, a nie ogólne rozwiązanie.
NaN nie jest równe niczemu
NaN („not a number”) to wartość, którą JavaScript zwraca, gdy operacja liczbowa nie ma sensownego wyniku: 0/0, Number("abc"), Math.sqrt(-1). Oba operatory równości zwracają false, gdy którakolwiek strona to NaN, także gdy obie strony to NaN:
Do wykrywania NaN używaj Number.isNaN(value). Nie używaj starszej globalnej funkcji isNaN: najpierw konwertuje ona argument, więc isNaN("hello") zwraca true, a to prawie nigdy nie jest to, czego chcesz.
Object.is: prawie ===, z dwiema poprawkami
Object.is(a, b) zachowuje się jak === poza dwoma przypadkami brzegowymi:
Zazwyczaj chcesz ===. Po Object.is sięgnij wtedy, gdy musisz traktować NaN jako równe samemu sobie albo odróżnić +0 od -0. Oba przypadki są rzadkie, ale czasem ważne w kodzie numerycznym i we wnętrzu frameworków (React używa Object.is do porównywania stanu).
Operatory nierówności dzielą się tak samo
!== jest ścisły, != luźny i obowiązuje ta sama rada:
Domyślnie używaj !==. Jeśli już dopuszczasz == null, możesz dopuścić też jego odpowiednik != null do sprawdzenia „ani null, ani undefined”.
Ściągawka do porównywania
Gdy musisz porównać dwie wartości, przejdź przez tę listę:
- Typy proste, spodziewasz się tego samego typu? Użyj
===. - Sprawdzasz null lub undefined?
x == nulljest w porządku, jeśli pozwala na to twój przewodnik stylu, w przeciwnym raziex === null || x === undefined. - Sprawdzasz
NaN?Number.isNaN(x). - Porównujesz obiekty po tożsamości?
===robi dokładnie to, czego chcesz. - Porównujesz obiekty po zawartości? Napisz funkcję pomocniczą, użyj biblioteki albo serializuj. Wbudowane operatory tu nie pomogą.
Trzymaj się ===, traktuj == jako specjalne narzędzie do przypadku == null, a ominiesz dziwactwa porównań, które wypełniają listy FAQ o JavaScript.
Dalej: operatory
Równość to tylko wycinek zestawu operatorów JavaScript. Następna strona omawia resztę: operatory arytmetyczne, logiczne, przypisania i krótsze formy, z których korzystasz w codziennym kodzie.
Najczęściej zadawane pytania
Czym różni się == od === w JavaScript?
=== to równość ścisła: zwraca true tylko wtedy, gdy oba operandy mają ten sam typ i tę samą wartość. == to równość luźna: przed porównaniem konwertuje operandy do wspólnego typu. 1 === '1' daje false, ale 1 == '1' daje true, bo == najpierw zamienia string na liczbę.
Czy w JavaScript zawsze używać ===?
Domyślnie tak. === jest przewidywalny, a jego zasady łatwo zapamiętać. Jedynym częstym wyjątkiem jest x == null, które wygodnie dopasowuje zarówno null, jak i undefined. Większość linterów (reguła eqeqeq w ESLint) wymusza === i pozwala opcjonalnie włączyć ten jeden wzorzec.
Dlaczego NaN === NaN zwraca false?
Specyfikacja IEEE 754 mówi, że NaN nie jest równe niczemu, nawet samemu sobie. Dlatego każdy operator równości zwraca false, gdy w porównaniu bierze udział NaN. Żeby sprawdzić, czy wartość to NaN, użyj Number.isNaN(x) albo Object.is(NaN, NaN), które zwraca true.
Jak porównać dwa obiekty w JavaScript?
Zarówno ==, jak i === porównują obiekty po referencji, a nie po zawartości. {a: 1} === {a: 1} daje false, bo to dwa różne obiekty. Żeby porównać wartości, napisz własne sprawdzenie, użyj biblioteki, np. isEqual z Lodash, albo w przypadku prostych obiektów zserializuj je przez JSON.stringify.