Menu
Русский

Операторы сравнения в JavaScript: ==, === и Object.is

Разбираемся, как в JavaScript работает сравнение: строгое и нестрогое равенство, подводные камни ==, поведение NaN и объектов, а также когда стоит взять Object.is.

Два способа спросить «А эти значения равны?»

В JavaScript есть два оператора сравнения на равенство: === (строгое равенство) и == (нестрогое равенство). Выглядят они почти одинаково. А ведут себя — совершенно по-разному.

index.js
Output
Click Run to see the output here.

=== проверяет, что оба операнда имеют одинаковый тип и одинаковое значение. == сначала приводит операнды к общему типу, а потом уже сравнивает. Именно это приведение типов — то самое злополучное coercion — и породило большую часть мемов про равенство в JavaScript.

Если коротко: по умолчанию используйте ===. Но длинную версию стоит прочитать хотя бы раз — чтобы понимать, от чего именно вы отказываетесь.

Строгое равенство: то, что вам почти всегда нужно

Строгое равенство === возвращает true только если совпадают и тип, и значение. Никаких неявных преобразований, никаких сюрпризов:

index.js
Output
Click Run to see the output here.

Если типы не совпадают, ответ сразу false. Если совпадают — JavaScript сравнивает значения. Для примитивов это сравнение по значению, а для объектов — по ссылке (об этом чуть ниже).

!== — это оператор строгого неравенства, он работает по тем же правилам, только наоборот.

Нестрогое равенство: приведение типов под капотом

== допускает, что с обеих сторон стоят значения разных типов. Перед сравнением он запускает целый танец с приведением типов, чтобы привести их к общему знаменателю:

index.js
Output
Click Run to see the output here.

Точные правила расписаны в спецификации, и сами по себе они не то чтобы жуткие — проблема в том, чтобы удержать их в голове посреди отладки. На том, что "0" == false даёт true, спотыкаются даже матёрые разработчики. То же самое с [] == false (тоже true: массив приводится к "", а тот — к 0).

Именно поэтому большинство стайл-гайдов — и правило eqeqeq в ESLint — советуют по умолчанию писать ===. Лишний символ в обмен на правила, которые реально запомнить.

Единственный полезный паттерн с ==

Есть ровно одна идиома с нестрогим равенством, которую стоит держать в арсенале: x == null вернёт true, если x — это null или undefined, и false во всех остальных случаях.

index.js
Output
Click Run to see the output here.

Строгий эквивалент — это x === null || x === undefined. Работает, но выглядит громоздко. Поэтому во многих кодовых базах == null оставляют как единственное узаконенное исключение. Главное — выбрать правило и придерживаться его.

Сравнение объектов в JavaScript идёт по ссылке

Когда речь о объектах, массивах и функциях, и ===, и == задают один и тот же вопрос: «Указывают ли обе стороны на один и тот же объект в памяти?» А вовсе не «одинаковое ли у них содержимое?»

index.js
Output
Click Run to see the output here.

Два объектных литерала с одинаковым содержимым — это всё равно два разных объекта. На этом хоть раз да спотыкается каждый.

Если нужно сравнивать объекты по значению, пишите свой хелпер, подключайте библиотеку (lodash.isequal) или, для простых plain-объектов, сериализуйте через JSON.stringify:

index.js
Output
Click Run to see the output here.

JSON.stringify годится только для простых данных — он игнорирует функции, undefined и символы, а порядок ключей в разных движках для некоторых структур не гарантируется. Это скорее быстрая проверка, чем универсальное решение.

Сравнение NaN в JavaScript

NaN (от «not a number») — это значение, которое JavaScript возвращает, когда у числовой операции нет осмысленного результата: 0/0, Number("abc"), Math.sqrt(-1). Оба оператора равенства возвращают false, если хотя бы с одной стороны стоит NaN — даже когда NaN с обеих сторон:

index.js
Output
Click Run to see the output here.

Чтобы понять, что перед нами NaN, используйте Number.isNaN(value). Старую глобальную функцию isNaN лучше не трогать — она сначала приводит аргумент к числу, поэтому isNaN("hello") вернёт true, а это почти наверняка не то, что вам нужно.

Object.is: почти ===, но с двумя поправками

Object.is(a, b) ведёт себя так же, как ===, за исключением двух пограничных случаев:

index.js
Output
Click Run to see the output here.

В большинстве случаев вам нужен именно ===. К Object.is стоит обращаться, когда нужно, чтобы NaN считался равным самому себе, либо чтобы отличить +0 от -0 — случаи редкие, но иногда критичные: в численных расчётах и во внутренностях фреймворков (React, например, использует Object.is для сравнения состояния).

Знак «не равно» в JavaScript работает по той же логике

!== — строгий, != — нестрогий, и рекомендация та же:

index.js
Output
Click Run to see the output here.

По умолчанию используйте !==. Если в вашем стайл-гайде разрешён == null, то и его «близнец» != null для проверки «не null и не undefined» тоже допустим.

Чек-лист для сравнения значений

Когда нужно сравнить два значения, пройдитесь по этому списку:

  • Примитивы одного типа? Берите ===.
  • Проверка на null или undefined? x == null вполне подойдёт, если стайл-гайд не против; иначе пишите x === null || x === undefined.
  • Проверка на NaN? Number.isNaN(x).
  • Сравниваете объекты по ссылке? === делает ровно то, что нужно.
  • Сравниваете объекты по содержимому? Напишите свою функцию, подключите библиотеку или сериализуйте значения. Встроенные операторы тут бессильны.

Держитесь ===, оставьте == как точечный инструмент для случая == null — и вы обойдёте стороной все те подводные камни равенства, которыми забиты FAQ по JavaScript.

Дальше: операторы

Операторы сравнения — это лишь одна грань операторов в JavaScript. В следующем материале разберём всё остальное: арифметические, логические, присваивания и сокращённые формы, которые пригодятся в повседневном коде.

Часто задаваемые вопросы

В чём разница между == и === в JavaScript?

=== — это строгое равенство: возвращает true, только если у операндов совпадает и тип, и значение. == — нестрогое равенство: перед сравнением оно приводит операнды к одному типу. Поэтому 1 === '1' даст false, а 1 == '1'true, потому что строка сначала преобразуется в число.

Стоит ли всегда использовать === в JavaScript?

По умолчанию — да. === предсказуем, и его правила легко держать в голове. Единственное распространённое исключение — проверка x == null, которая одним махом ловит и null, и undefined. Большинство линтеров (правило eqeqeq в ESLint) требует именно === и разрешает этот паттерн как исключение.

Почему NaN === NaN возвращает false?

По стандарту IEEE 754 NaN не равен ничему, включая самого себя. Поэтому любое сравнение с NaN через операторы равенства вернёт false. Чтобы проверить, является ли значение NaN, используйте Number.isNaN(x) или Object.is(NaN, NaN) — последний, кстати, возвращает true.

Как сравнить два объекта на равенство в JavaScript?

И ==, и === сравнивают объекты по ссылке, а не по содержимому. {a: 1} === {a: 1} даёт false, потому что это два разных объекта в памяти. Для сравнения по значению придётся написать свою функцию, взять что-то вроде isEqual из Lodash или, для простых plain-объектов, сравнить результат JSON.stringify.

Учитесь программировать с Coddy

НАЧАТЬ