06 — Equality — == vs === vs Object.is, and Four Algorithms
"Always use triple equals, move on" was my equality strategy, and it worked until it couldn't explain an edge case. The idea that everything else hangs off: there isn't one equality in JavaScript, there are four algorithms under the hood, and ==, ===, and Object.is each pick a different one — differing only on two edge cases. [1]
The framing that finally landed is the ladder of strictness. Reading from loosest to strictest:
The two operators
== (loose equality) compares two values, performing type coercion if the types differ [2]. "5" == 5 is true because the string is converted to a number first. null == undefined is true (a special-case pair). This is the operator that produces "surprising" results — "0" == false is true, "" == 0 is true — because the Abstract Equality Comparison algorithm coerces both sides through a fixed set of rules. My rule is simple: never write ==. The surprises aren't worth the keystroke.
=== (strict equality) compares without coercion [3]. If the types differ, it returns false immediately. Same type, compare values directly. The one famous wrinkle: NaN === NaN is false, and +0 === -0 is true. This is the default for almost all equality checks.
The reason === wins isn't that it's stricter for its own sake — it's that the result is predictable. With == I had to hold the coercion table in my head; with === the question "are these the same type and value?" answers itself.
Object.is — the strictest
Object.is() is the fourth algorithm, SameValue [4]. It behaves like === except it fixes the two edge cases the spec calls out:
- Object.is(NaN, NaN) → true (where NaN === NaN is false)
- Object.is(+0, -0) → false (where +0 === -0 is true)
In normal code these differences almost never matter — but when they do, Object.is is the only correct tool. The classic case is NaN: x === NaN is always false, so the idiomatic check is Number.isNaN(x) or, equivalently, Object.is(x, NaN).
SameValueZero — the one you can't call directly
The fourth algorithm, SameValueZero, is the same as Object.is except it treats +0 and -0 as equal [5]. It's not exposed as a function — it's used internally by Map key comparison, Set membership, and Array.prototype.includes(). That's why [NaN].includes(NaN) is true but [NaN].indexOf(NaN) is -1 (indexOf uses ===). When a Set says "I already have this value," it's using SameValueZero under the hood.
The practical upshot: collection lookups are slightly smarter than ===. If I'm checking whether a value is in an array and NaN is a possibility, arr.includes(x) is correct where arr.indexOf(x) !== -1 is not.
How I use this
The rules I actually apply: default to === for every comparison; reach for Object.is only when NaN or signed-zero is genuinely in play (and Number.isNaN for the NaN check reads cleaner); never write ==; and remember that Array.includes, Set, and Map use SameValueZero, which is more forgiving than === on NaN. That covers 100% of the equality decisions in real code. The four-algorithm view matters because it explains why the four differ — and once I could see the two edge cases (NaN and signed zero) as the only variables, equality stopped being a source of doubt.
References
[1] Mozilla, "Equality comparisons and sameness," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
[2] Mozilla, "Equality (==) operator," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality
[3] Mozilla, "Strict equality (===) operator," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality
[4] Mozilla, "Object.is() — same-value equality," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
[5] Mozilla, "Same-value-zero equality," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#same-value-zero_equality
Knowledge check · Question 1 of 5
Why is `==` generally avoided in favor of `===`?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!