Equality Demystified: Strict (===), Loose (==), and Object.is()
Master equality comparisons. Understand the loose equality coercion algorithm, strict type checking, and the precise mathematical distinctions of Object.is().
By the end, you’ll be able to…
- Contrast Loose Equality (==), Strict Equality (===), and SameValueZero (Object.is).
- Dissect the edge cases of NaN and signed zeros (+0 vs -0).
- Understand why == creates security and maintenance vulnerabilities in production.
01The Three Tiers of Equality in JavaScript
Equality comparison in JavaScript is a frequent battleground for bugs. The language actually provides three distinct equality evaluation algorithms: Loose Equality (==, also called Abstract Equality), Strict Equality (===), and SameValue / SameValueZero (accessible via Object.is()).
The fundamental difference between loose and strict equality is that loose equality (==) allows implicit type coercion before comparing values, whereas strict equality (===) immediately returns false if the operands have different types. Because loose equality performs complex multi-step conversions, it can lead to deeply counter-intuitive results like '' == 0 being true, false == '0' being true, and null == undefined being true.
In the official ECMAScript specification, equality is not a single monolithic operation. The specification explicitly defines four distinct equality comparison algorithms: IsLooselyEqual (==), IsStrictlyEqual (===), SameValue (Object.is), and SameValueZero (used by Map, Set, and Array.prototype.includes).
Knowing which algorithm executes in which language construct is crucial. For example, while [NaN].indexOf(NaN) uses IsStrictlyEqual and returns -1 (because NaN === NaN is false), [NaN].includes(NaN) uses SameValueZero and correctly returns true. Understanding these four algorithms transforms confusing JavaScript quirks into predictable, deterministic behavior.
02The Strict Equality Algorithm (===)
Strict equality (===) is the foundational standard for modern JavaScript engineering. The algorithm is refreshingly simple: if Type(x) is different from Type(y), return false. If the types match, numbers are compared by mathematical value, strings by identical character sequences, booleans by matching states, and objects by reference identity.
However, strict equality has two infamous mathematical anomalies defined by the IEEE 754 floating-point standard: First, NaN === NaN evaluates to false. NaN is the only value in JavaScript that is not strictly equal to itself. Second, +0 === -0 evaluates to true, even though positive and negative zero have different representations and behaviors in calculus and physics.
03Object.is() and SameValue Equality
To solve the anomalies of strict equality, ECMAScript 2015 introduced Object.is(). Object.is() evaluates the SameValue algorithm: it behaves identically to === in almost all scenarios, but with two precise corrections.
First, Object.is(NaN, NaN) evaluates to true. Second, Object.is(+0, -0) evaluates to false. This makes Object.is() the algorithm of choice inside reactive frameworks like React and Vue when performing dependency change detection (Object.is determines whether a hook or component needs to re-render).
04Why This Matters in Production
In production web applications, using loose equality (==) frequently creates severe security and data integrity vulnerabilities. For example, comparing an authentication token or user role with if (role == false) can accidentally match empty strings or zeros due to unintended coercion.
Modern static analysis tools (ESLint) strictly enforce the eqeqeq rule: always use === and !==. When inspecting numerical calculations for division by zero (where 1 / -0 yields -Infinity while 1 / +0 yields +Infinity), leverage Object.is() for exact mathematical verification.
The ideas worth keeping.
- Loose equality (==) coerces types before comparison; strict equality (===) requires identical types.
- NaN is not equal to anything, even itself (NaN === NaN is false); use Number.isNaN() or Object.is().
- Object.is() provides mathematically rigorous SameValue comparison, treating NaN as equal and distinguishing +0 from -0.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Exercise 1: Robust NaN-Safe Equality Checker
Write a function safeEqual(a, b) that performs strict equality but correctly returns true if both arguments are NaN.
Explore a worked solution
function safeEqual(a, b) { return Object.is(a, b);}Object.is accurately considers NaN identical to NaN while avoiding the loose coercion traps of the == operator.
Exercise 2: Detecting Negative Zero
Write a function isNegativeZero(val) that accurately returns true only if the input value is -0.
Explore a worked solution
function isNegativeZero(val) { return Object.is(val, -0);}Object.is(+0, -0) is false, making it the most direct and accurate way to detect -0 without checking 1 / val === -Infinity.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Exercise 1: Robust NaN-Safe Equality Checker
Write a function safeEqual(a, b) that performs strict equality but correctly returns true if both arguments are NaN.
[Process exited with code 0 - No output]
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
One lesson closer.
Reading is a start. Trying it yourself makes the connection.