JavaScript Equality Table
| Expression | Result | Why |
|---|---|---|
1 == '1' | true | == coerces types before comparing - the source of endless bugs |
1 === '1' | false | === compares type first; different types are never equal |
null == undefined | true | The one special pair == treats as equal |
null === undefined | false | Different types - strict never bends |
NaN === NaN | false | NaN is not equal to anything, including itself - use Number.isNaN |
0 == '' | true | Both coerce to 0 - the empty string trap |
[] == false | true | Empty array coerces to empty string, then to 0 |
Object.is(NaN, NaN) | true | The third comparison - SameValue, stricter than === |
JavaScript has three equality operators - ==, ===, and Object.is - and they disagree on purpose. The loose operator converts before comparing; the strict operator refuses to convert; Object.is applies the strictest definition including how NaN treats itself. The table below is the gallery of results that decide which one your code should use.
Bottom line: the team rule that ends the debate is always ===, never == - with one idiomatic exception, x == null as shorthand for "null or undefined". The loose operator's coercion table is the source of the language's most famous surprises: 0 == '' is true, [] == false is true, and '1' == 1 hides a type error until production. Strict comparison makes those bugs compile-time visible instead.
The honest part: === has exactly one blind spot - NaN is not equal to itself, by IEEE 754 design (NaN means "not a number", and two unknowns are not the same unknown). That is why Number.isNaN exists for detection and Object.is for comparison: Object.is(NaN, NaN) is true, making it the SameValue semantics the language uses internally for things like array index changes in frameworks.
How to use
- Default to === in all code; reach for the table when you meet a == result you did not expect.
- Use Number.isNaN(x) to detect NaN - x === NaN is always false, the only value with that property.
- Know the one idiomatic == : x == null is the compact "null or undefined" test; write it longhand if your style guide prefers.
Frequently asked questions
What is the actual difference between == and ===?
== converts both sides to a common type before comparing ("1" == 1 is true because the string becomes a number); === compares types first and returns false immediately for different types. Every surprise in the table - 0 == '', [] == false, null == undefined - is the conversion rules at work. === has exactly two special cases of its own: +0 and -0 are equal, and NaN is not. Once types match, the two operators agree.
Why does NaN not equal NaN?
Because NaN means "an unspecified invalid number" - and IEEE 754, the floating-point standard JavaScript follows, defines that two unspecified values cannot be claimed equal. The practical consequence: x === NaN is always false even when x IS NaN, which is why the language provides Number.isNaN(x) for detection. The deeper point: NaN usually arrives as the result of a failed parse or 0/0 - the right fix is usually upstream, not a cleverer comparison.
Is x == null acceptable if I always use ===?
It is the one widely accepted exception: == null is exactly true for null and undefined and false for everything else - including 0 and empty string, which == null would wrongly accept in its looser moments. It works because null and undefined are the only pair the loose operator special-cases. Many style guides still require the explicit x === null || x === undefined; the compact form is not wrong, it is a documented idiom - pick one and lint for it.
When do I need Object.is instead of ===?
Two places: NaN comparison - Object.is(NaN, NaN) is true where === is false - and signed zero - Object.is(0, -0) is false where === is true. React's dependency comparison uses Object.is semantics for exactly these edge cases, which is why a state update with NaN behaves differently from one with a normal number. Outside framework internals and numeric code that distinguishes signed zero, === plus Number.isNaN covers everything Object.is does with less vocabulary.