Object.is vs ===
Object.is vs === for NaN and ±0 — SameValue vs Strict Equality, and when each shows up in real checks.
- javascript
- object-is
Most of the time === is what you want. Object.is implements the SameValue algorithm — almost ===, except two cases people trip on: NaN and signed zero.
NaN === NaN; // false
Object.is(NaN, NaN); // true
+0 === -0; // true
Object.is(+0, -0); // false
Side-by-side
| A | B | === |
Object.is |
|---|---|---|---|
1 |
1 |
true | true |
'a' |
'a' |
true | true |
{} |
{} |
false | false |
NaN |
NaN |
false | true |
+0 |
-0 |
true | false |
null |
undefined |
false | false |
No coercion. Both are strict about types.
Why NaN === NaN is false
IEEE 754 and the language define NaN as unordered / not equal to anything, including itself. That breaks naive “did this fail?” checks:
const x = 0 / 0;
if (x === NaN) { /* never */ }
if (Number.isNaN(x)) { /* yes */ }
if (Object.is(x, NaN)) { /* yes */ }
Prefer Number.isNaN for “is this NaN?” — it doesn’t coerce (unlike global isNaN('foo')).
When Object.is matters
- Maps / Sets / React style “same value” for dependency comparison edge cases
- Implementing polyfills that match spec SameValue
- Distinguishing
+0and-0(rare: some math / canvas / divide-by-zero sign)
1 / +0; // Infinity
1 / -0; // -Infinity
Object.is(1 / +0, 1 / -0); // false
Day-to-day app code: === + Number.isNaN covers almost everything.
SameValueZero (Maps and Sets)
Map/Set key equality uses SameValueZero: like Object.is but +0 and -0 are equal, and NaN equals NaN.
const s = new Set([NaN, +0]);
s.has(NaN); // true
s.has(-0); // true — SameValueZero
s.size; // 2 if you also add something else… Set([NaN,+0,-0]) size 2
Know the three names:
- Strict Equality (
===) - SameValue (
Object.is) - SameValueZero (Map/Set keys)
Interview answer (out loud)
“
Object.isis like===exceptObject.is(NaN, NaN)is true andObject.is(+0, -0)is false. I still use===daily andNumber.isNaNfor NaN checks. Map and Set use SameValueZero, where NaN matches NaN and zeros are not distinguished.”
Implementing polyfills carefully
function objectIs(a, b) {
if (a === b) {
// true for +0/-0 together — fix zeros
return a !== 0 || 1 / a === 1 / b;
}
// false for NaN===NaN — fix NaN
return a !== a && b !== b;
}
This is the classic polyfill shape — good whiteboard material.
React dependency arrays
React’s Object.is comparison for hooks deps means NaN dependencies won’t retrigger forever the way === might suggest, and +0/-0 are distinguished. Rare in UI state; know it exists when debugging pathological numeric state.
Don’t switch the whole codebase to Object.is
Readability and convention matter: === is the lingua franca. Use Object.is when the NaN or signed-zero distinction is the bug you’re fixing.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.