Czym różni się == od === w JavaScript
Klasyczne tłumaczenie różnicy mówi, że == sprawdza wartości, a === typ i wartość. Spec mówi co innego - oba sprawdzają typ. Ta notatka jest o tym, na czym ta różnica naprawdę polega.
Te cztery linijki wyglądają niewinnie, ale popularny opis ich różnicy jest niepełny:
1 == "1"; // true
1 === "1"; // false
1 == 1; // true
1 === 1; // trueNajczęściej słyszysz: == patrzy na wartość, === patrzy na typ i wartość. Tłumaczy to pierwsze dwie linijki, ale nie mówi prawdy o tym, jak operatory działają w środku. Oba sprawdzają typ. Prawdziwa różnica leży gdzie indziej.
#Co naprawdę robi ===
Operator === (strict equality) działa najprościej z możliwych:
- Jeśli typy obu stron są różne - zwróć
false. Koniec. - Jeśli typy są te same - porównaj wartości.
Nie ma tu żadnej konwersji, żadnych ścieżek dla obiektów, żadnego algorytmu w tle. 1 === "1" to false, bo typy są różne. 1 === 1 to true, bo typy się zgadzają i wartości też.
#Co naprawdę robi ==
Operator == (loose equality) jest pozornie inny, ale w specyfikacji znajdziesz coś, czego nie spodziewasz się po pierwszym akapicie. Jego algorytm - IsLooselyEqual - zaczyna się od jednego zdania:
If SameType(x, y) is true, then return IsStrictlyEqual(x, y).Czyli: gdy typy są te same, == po prostu uruchamia === - to ta sama operacja silnika, tylko inaczej zapisana w kodzie.
Cała różnica zaczyna się dopiero, gdy typy są różne. Wtedy === od razu zwraca false, a == próbuje pogodzić strony przez konwersję - czyli coercion. Pomysł jest taki: skoro chcesz porównać liczbę ze stringiem, ja się tym zajmę. 1 == "1" zwraca true, bo silnik zamienia "1" na 1 i porównuje liczby.
To jest cała różnica. Jeden operator dopuszcza coercion, drugi go odrzuca.
#Trzy reguły, które wypadają z tej jednej różnicy
Znaj swoje typy. Jeśli wiesz, co masz po obu stronach, wybór operatora sprowadza się do tego, czy chcesz konwersji. Jeśli nie wiesz - żaden operator cię nie uratuje, problem jest wcześniej, w samym kodzie.
Gdy typy są te same - bez znaczenia. userId === otherUserId i userId == otherUserId to to samo wywołanie. Nie ma niespodzianek po stronie ==, bo nie ma coercion.
x == null to wyjątek wart zapamiętania. Jedyny przypadek, w którym == daje czytelniejszy kod niż ===. Sprawdza w jednym wyrażeniu, czy x to null albo undefined, i tylko to - żadna inna wartość nie wpadnie tu jako true.
#Co z performance
Krąży opinia, że jeden z tych operatorów jest wyraźnie szybszy. W praktyce V8 i tak zoptymalizuje obie wersje do identycznej formy, gdy typy są te same. Faktem pozostaje, że == na różnych typach dorzuca algorytm coercion - parę kroków więcej niż ===, który od razu zwraca false. Ale liczone jest to w nanosekundach na operację. Operator wybierasz dla intencji w kodzie, nie dla milisekund.
#Słowo na koniec
== i === różnią się tylko jedną rzeczą: czy dopuszczają coercion, kiedy typy się rozjeżdżają. Reguła „zawsze używaj ===” to dobry domyślny wybór - eliminuje pytanie „czy znam typy?”. Ale to nie znaczy, że == jest pułapką. Jest narzędziem, którego nie używasz, jeśli typów nie znasz. Krócej: znaj swoje typy.
Co dokładnie silnik robi w tym „spróbuj pogodzić strony” - czyli jak działa coercion krok po kroku - opisałem w notatce o coercion.