08 / 20 · Day 3
Day 3 · Concept 08

The === rule

=== compares without coercion: same type, same value. == coerces — and produces surprises. The rule is simple: always ===, with one carefully-justified exception.


1 · The surprises of ==

js equality.js
// All true with ==
console.log("" == false);       // true
console.log(0 == "0");           // true
console.log(0 == "");            // true
console.log("0" == false);       // true
console.log(null == undefined);  // true

// All false with ===
console.log("" === false);       // false
console.log(0 === "0");          // false
console.log(null === undefined); // false

Coercion rules in == are non-obvious. Use ===; you keep your sanity.

2 · The single useful == case

js loose-null.js
function isNullish(x) {
    return x == null;   // true for both null AND undefined
}

console.log(isNullish(null));        // true
console.log(isNullish(undefined));   // true
console.log(isNullish(0));            // false
console.log(isNullish(""));           // false

x == null is the idiomatic "is this nullish?" check. Some style guides still ban it; others permit it. x === null || x === undefined works too. Or use x ?? defaultValue (next concept).

3 · Object identity vs structural equality

js object-eq.js
// Objects compare by reference
console.log({a: 1} === {a: 1});       // false — different objects
const x = {a: 1};
console.log(x === x);                  // true — same object

// Structural — write your own or use a library
function shallowEqual(a, b) {
    const ka = Object.keys(a), kb = Object.keys(b);
    if (ka.length !== kb.length) return false;
    return ka.every(k => a[k] === b[k]);
}
console.log(shallowEqual({a: 1}, {a: 1}));   // true
For deep comparisons use JSON.stringify(a) === JSON.stringify(b) (quick but doesn't handle Maps/Sets/dates well), or a library like lodash.isEqual, or write your own recursive walker.

4 · NaN — the one that isn't equal to itself

js nan.js
console.log(NaN === NaN);          // false
console.log(Number.isNaN(NaN));    // true — the correct test
console.log(isNaN("abc"));         // true — but coerces! "abc" is not NaN per se
console.log(Number.isNaN("abc"));  // false — strict test

Use Number.isNaN(x), not the global isNaN(x). The global one coerces.

5 · Common mistakes

  • Using == in conditions you don't 100% understand. Always === until you have a reason.
  • Comparing objects with === expecting deep equality. It's reference equality.
  • Forgetting Number.isNaN over isNaN.
  • typeof null === "object" — historic; test for null with x === null.

6 · When it clicks

  • You never type == by accident.
  • You reach for x == null intentionally as the nullish check (or ?? ).
  • You know object equality means reference equality without thinking.
Found this useful?