Equality in JavaScript: ==, ===, Object.is and SameValueZero
JavaScript has four kinds of equality. Learn the == steps, why NaN !== NaN, how +0 and -0 differ, and which one includes, Map and Set use.
Comes up in: JavaScript, Recruiter screen
Learn#
Kiran’s analytics filter removes bad rows with rows.filter((r) => r.value !== NaN). Nothing is removed. NaN is not equal to anything — not even itself — so that check is always true.
JavaScript has four equality rules:
- Loose equality
==converts types first:nullandundefinedequal each other and nothing else; a boolean becomes a number; a string compared with a number becomes a number; an object becomes a primitive.
- Strict equality
===never converts. Different types are never equal.
- SameValue, used by
Object.is, is===with two fixes:NaNequalsNaN, and+0and-0are different.
- SameValueZero is like SameValue, but
+0equals-0.includes,MapandSetuse it.
console.log(null == undefined, null == 0, "1" == 1, [] == false, [] == ![]);
console.log(NaN === NaN, Number.isNaN(NaN), +0 === -0, Object.is(+0, -0), Object.is(NaN, NaN));
console.log([NaN].includes(NaN), [NaN].indexOf(NaN), new Set([+0, -0]).size);Output (verified)
true false true true true false true true false true true -1 1
indexOf uses ===, so it cannot find NaN; includes can. -0 appears when you round a small negative number, like Math.round(-0.4).
left
- []
right
- false
[] == ![] — first, ![] is evaluated. [] is truthy, so ![] is false.
Read the steps as text
- [] == ![] — first, ![] is evaluated. [] is truthy, so ![] is false.
- A boolean on one side is converted to a number: false → 0.
- Object vs number: the object goes through ToPrimitive. [] → ''.
- String vs number: the string becomes a number. '' → 0. Now 0 == 0 is true.
Predict#
Predict first
null == 0 — true or false? (Hint: null >= 0 is true.)
== has a special rule: null only equals null or undefined, so null == 0 is false. But >= uses ToNumber, where null is 0, so null >= 0 is true. The two operators follow different rules.
Interview angle#
Say it like this — about 30 seconds
“
===compares without converting.==converts first: null and undefined only equal each other, booleans become numbers, strings compared with numbers become numbers, and objects become primitives.Object.isis like===but treats NaN as equal to itself and +0 and -0 as different. SameValueZero, used byincludes, Map and Set, also matches NaN but treats the zeros as equal. I use===by default,== nullto catch null or undefined, andNumber.isNaNfor NaN.”
Follow-up Why is [] == false true?
Follow-up How do you check for NaN?
Number.isNaN(x). The global isNaN converts first, so isNaN("abc") is true for a string.Follow-up What is the difference between Object.is and ===?
Object.is(NaN, NaN) is true, and Object.is(+0, -0) is false.Practice#
On your own
Implement the spec’s SameValue and SameValueZero using only === and arithmetic.
Done when
sameValue(NaN, NaN)is true andsameValue(0, -0)is false.sameValueZero(NaN, NaN)andsameValueZero(0, -0)are both true.- Everything else matches
===, including objects compared by identity.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Which values does null == x match?
Only null and undefined.
How does Object.is differ from ===?
Object.is(NaN, NaN) is true; Object.is(+0, -0) is false.
Which equality do includes, Map and Set use?
SameValueZero: NaN matches NaN, +0 matches -0.
How do you reliably test for NaN?
Number.isNaN(x).
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Use ===. Use Number.isNaN for NaN. Remember includes/Map/Set find NaN, and indexOf does not.
Check yourself: Can [NaN].indexOf(NaN) find it?
No — indexOf uses ===. Use includes.
How it works underneathSenior rounds
== follows IsLooselyEqual, a short list of conversion steps Source: ECMA-262: IsLooselyEqual. Object.is uses SameValue, and collections use SameValueZero Source: MDN: Equality comparisons and sameness.
Check yourself: What is the spec name of the == algorithm?
IsLooselyEqual.
Edge cases and trapsWhere people slip
1 / -0 is -Infinity, which is how old code told the zeros apart. JSON.stringify(-0) is “0”. Objects are equal only to themselves under every rule — two arrays with the same items are never ==.
Check yourself: How can you tell -0 from +0 without Object.is?
1 / x is -Infinity for -0 and Infinity for +0.
In productionStaff-level answers
Turn on ESLint eqeqeq with { null: "ignore" } to allow == null. React’s useEffect dependencies and useMemo compare with Object.is — so a new array or object every render always counts as a change (RCT.03).
Check yourself: Which ESLint rule enforces ===, while allowing == null?
eqeqeq with the "smart" or { null: "ignore" } option.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Explain how
==works. Why is[] == falsetrue? - What is the difference between
Object.isand===? - What is
NaN? How do you check for it?