JavaScriptComparison Operators

Comparison Operators

Comparison operators answer yes/no questions about two values. They power every if, every filter, every "is this the same?" check. JavaScript has two equality operators (== and ===), four ordering operators (<, >, <=, >=), and a handful of edge cases — NaN, -0, mixed types — that are worth knowing about.

Strict equality (===, !==)

=== is true if both operands have the same type and the same value. No coercion. This is the equality you want 95% of the time.

JS
3 === 3;          // true
"hi" === "hi";    // true
true === true;    // true
null === null;    // true
undefined === undefined; // true

3 === "3";        // false — different types
0 === false;      // false
null === undefined; // false

3 !== "3";        // true
Loose equality (==, !=)

== performs type coercion before comparing. The rules are stable but unintuitive in places.

JS
3 == "3";          // true   — string converted to number
0 == false;        // true   — boolean converted to number
"" == 0;           // true   — empty string converted to number
null == undefined; // true   — special case
[] == false;       // true   — array → "" → 0 → false
[1] == 1;          // true   — array → "1" → 1
"1\n" == 1;        // true   — whitespace ignored when parsing as number

NaN == NaN;        // false  (!) — NaN is never equal to anything, including itself
null == 0;         // false  — null only loosely equals undefined
The one useful loose-equality pattern
`x == null` is the idiomatic way to test "null **or** undefined" in one shot. Linters like ESLint's `eqeqeq` rule typically allow this exception.

JS
function greet(name) {
  if (name == null) {        // catches both null and undefined
    name = "stranger";
  }
  console.log("Hello, " + name);
}
Equality algorithms in the spec

Three algorithms live in the spec, all of which you can think of in plain English:

  • Strict Equality (===) — same type, same value. NaN is never equal to anything. +0 === -0 is true.

  • Loose Equality (==) — if types differ, coerce by these rules: numbers and strings → both numbers; booleans → numbers; objects compared to primitives → unbox the object with valueOf/toString. Then apply strict equality.

  • SameValue (Object.is) — like ===, but NaN equals NaN and +0 does not equal -0. Used internally by Map, Set and React.

Ordering operators

JS
5 > 3;       // true
5 >= 5;      // true
3 < 5;       // true
3 <= 2;      // false

// Strings compare lexicographically by code unit (roughly: dictionary order)
"apple" < "banana";   // true
"Z" < "a";            // true  — uppercase letters have lower code points
"10" < "9";           // true  — string comparison, "1" < "9"
"10"  <  9;           // false — number comparison, 10 < 9 is false

// Mixed types are coerced to numbers
"5" > 3;     // true
"abc" > 1;   // false — Number("abc") is NaN, all comparisons with NaN are false

Comparing strings to numbers coerces the string to a number. Comparing two strings does not coerce — it walks character by character.

Sort orders need a comparator
The default `arr.sort()` converts each element to a string and sorts lexicographically — so `[1, 10, 2].sort()` gives `[1, 10, 2]`. For numeric order, pass a comparator: `arr.sort((a, b) => a - b)`.
NaN comparisons

NaN is the only value that is not equal to itself. Every comparison involving NaN is false.

JS
NaN === NaN;     // false
NaN == NaN;      // false
NaN > 0;         // false
NaN < 0;         // false
NaN >= NaN;      // false
NaN <= NaN;      // false

Number.isNaN(NaN); // true   — the safe test
Number.isNaN("oops"); // false — only matches the actual NaN value

Always test for NaN with Number.isNaN rather than x === NaN (which is always false).

Object.is

Object.is(a, b) is almost identical to ===, with two corrections:

JS
Object.is(NaN, NaN);   // true   — === would be false
Object.is(+0, -0);     // false  — === would be true
Object.is(2, 2);       // true
Object.is("a", "a");   // true
true
false
true
true

This matches the SameValue algorithm and is what Map keys, Set membership and React state equality use under the hood.

Objects and reference equality

Two objects compare equal only if they are the same reference. There is no built-in "deep equality" operator in JavaScript.

JS
const a = { x: 1 };
const b = { x: 1 };
const c = a;

a === b;   // false — different objects, same contents
a === c;   // true  — same reference

// Compare contents — quick-and-dirty
JSON.stringify(a) === JSON.stringify(b);  // true, but slow and limited

// For real apps, use a deep-equal helper from a library (lodash isEqual, dequal)
Chained comparisons don't work like math

JS
let x = 5;

// LOOKS like "is x between 1 and 10?"
1 < x < 10;        // true — but for the wrong reason!

// Parses as ((1 < x) < 10) → (true < 10) → (1 < 10) → true
// Try this:
1 < x < -1;        // ALSO true!  (true < -1 → 1 < -1 → false)
                   // wait — that's false, let me redo it

// Either way, the result has nothing to do with "between"
Use && for ranges
Write `1 < x && x < 10` explicitly. Chained comparisons are a footgun — they parse left-to-right and the intermediate boolean gets coerced into the next comparison.
Practical recipes

Common patterns

JS
// Check for null or undefined
if (value == null) { /* both null and undefined */ }

// Range check
if (age >= 18 && age < 65) { /* working age */ }

// Empty string / empty array
if (str === "") { /* explicit empty string */ }
if (arr.length === 0) { /* empty array */ }

// Compare two arrays element-by-element
function arraysEqual(a, b) {
  return a.length === b.length && a.every((v, i) => v === b[i]);
}

// Find a duplicate
new Set(arr).size !== arr.length;  // true if there are duplicates
The one-line rule
Use `===` everywhere except `x == null`. That single guideline avoids almost every comparison pitfall the language has to offer.