Каждый раз я с опаской заменяю ==
на ===
— боюсь сломать логическое выражение. Ведь оператор равенства ==
реализует много неявной логики по сравнению значений, чем мог воспользоваться предыдущий разработчик.
Операторы ==
и ===
проверяют значения на совпадение, используя различные определения совпадения. Оператор идентичности (identity) ===
проверяет операнды на «идентичность», руководствуясь строгим определением совпадения. Оператор равенства (equality) ==
проверяет по менее строгим правилам, допускающим преобразования типов.
Оператор идентичности ===
вычисляет значения своих операндов, а затем сравнивает, без преобразования типов. Он руководствуется правилами:
- Если у значений разные типы — они не идентичны.
- Если оба значения или
null
илиundefined
— они идентичны. - Если оба значения или
true
илиfalse
— они идентичны. - Если одно или оба значения —
NaN
— они не идентичны. (ЗначениеNaN
никогда не идентично никакому значению, даже самому себе. Чтобы проверить значениеx
наNaN
, используйте выражениеx !== x
. Только дляNaN
такая проверка вернетtrue
). - Если оба операнда это числа с одним и тем же значением — они идентичны. Если одно число равно
0
, а другое-0
, они также идентичны. - Если оба значения это строки и содержат одни и те же 16-битные значения в одинаковых позициях — они идентичны. Две строки могут иметь один и тот же смысл и одинаково выглядеть на экране, но содержать отличающиеся последовательности 16-битных значений. Интерпретатор JavaScript не выполняет нормализацию символов юникода, поэтому подобные пары строк не считаются операторами
===
и==
ни равными, ни идентичными. - Если оба значения ссылаются на один и тот же объект, массив или функцию — они идентичны. Если они ссылаются на различные объекты — они не идентичны, даже при идентичных свойствах.
Оператор равенства ==
похож на оператор идентичности, но он использует менее строгие правила. Если у значений разные типы — они преобразуются и сравниваются:
- Если у значений одинаковый тип, они проверяются на идентичность, как описано выше.
- Если значения не относятся к одному типу, оператор
==
считает их равными, при следующих правилах:- Если одно значение
null
, а другоеundefined
— они равны. - Если одно значение число, а другое строка, то строка преобразуется в число и выполняется сравнение.
- Если одно значение —
true
, оно перед сравнением преобразуется в1
. Если —false
, оно преобразуется в0
и сравнение выполняется снова. - Если одно значение число или строка, а другое — объект, то перед сравнением объект преобразуется в простой тип. Встроенные классы преобразуются методом
valueOf()
, если не получилось, тоtoString()
. КлассDate
всегда выполняет преобразованиеtoString()
. Не базовые объекты джаваскрипта сами определяют способ преобразования в простые типы. - Любые другие комбинации значений не равны.
- Если одно значение
Правила преобразования типов и сравнения значений для оператора равенства ==
сложные и труднозапоминаемые. Интересные случаи:
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
Особый случай — сравнение литерал с объектом:
"abc" == new String("abc") // true
"abc" === new String("abc") // false
Здесь, оператор ==
проверяет значение объектов и возвращает true
. Оператор ===
возвращает false
, т.к. у объектов различные типы. Какое поведение корректно? Зависит от того, что сравнивать. Но лучше обойти вопрос, и не использовать конструктор для создания строковых объектов.
В заключении — таблицы сравнения значений для операторов равенства и идентичности с сайта dorey.github.io.
Для оператора ==
(или !=
):
Для оператора ===
(или !==
):
Из таблиц следует вывод, что поведение ==
не очевидно и может только запутать. Используя ===
можно быть уверенным в возвращаемом значении.
Интересная статья: When is it OK to use == in JavaScript?