Equality & Coercion: == vs ===

Beginner · 20 min read · ▶ live playground · ✦ checkpoint

JavaScript gives you two ways to ask "are these equal?" — and the difference between them has caused more real-world bugs than any other corner of the language. This lesson teaches you what coercion actually is, which values secretly count as false, and the one simple rule professionals follow.

Two kinds of equality

Strict equality === asks: same type and same value? If the types differ, the answer is false — no negotiation. Loose equality == is friendlier and stranger: if the types differ, it first coerces (silently converts) one or both values, then compares.

console.log(5 === 5);     // true   same type, same value
console.log("5" === 5);   // false  string vs number — done, no conversion
console.log("5" == 5);    // true   "5" is coerced to the number 5 first
console.log(0 == false);  // true   false is coerced to the number 0
console.log("" == 0);     // true   "" also coerces to 0

→ With ==, three very different values — 0, "", and false — all quietly agree with each other.

Try it — a real console

This playground is the actual JavaScript engine in your browser — nothing simulated. Edit the code and hit Run; the last expression echoes its value like DevTools does. Then type your own experiments at the prompt — "" == false, "0" == 0, whatever you're suspicious of. The engine is the ultimate authority.

javascript — live consolereal engine
⌘/Ctrl + Enter to run

Truthy, falsy & friends

Coercion also decides what happens when a non-boolean is asked to act like one — which is exactly what if will do to your values in the next lesson. Every value in JavaScript is either truthy or falsy, and the falsy list is short enough to memorize. These eight, and nothing else:

// The complete falsy list — Boolean() shows how any value converts:
console.log(Boolean(false));     // the boolean itself
console.log(Boolean(0));         // zero
console.log(Boolean(-0));        // negative zero
console.log(Boolean(0n));        // BigInt zero
console.log(Boolean(""));        // empty string
console.log(Boolean(null));      // no value
console.log(Boolean(undefined)); // never set
console.log(Boolean(NaN));       // not-a-number

→ eight lines of false. Every other value in the language — every one — converts to true.

Everything else is truthy, including some famous surprises:

  • "0" and "false" are truthy — any non-empty string is.
  • [] and {} are truthy — an empty container is still a container.
  • …and yet [] == false is true, because == coerces the array to "" first. Truthiness and loose equality follow different rules — a beautiful extra reason to avoid ==.

The rules professionals follow

After all that, the working rule is refreshingly simple — it's what every serious style guide and linter enforces:

  • Always use === and !==. Say what you mean; let no conversion happen behind your back.
  • One sanctioned exception: x == null is true exactly when x is null or undefined — a deliberate, idiomatic "is this missing?" check. Use it knowingly or not at all.
  • For the edge cases, Object.is() tells the full truth: Object.is(NaN, NaN) is true (while NaN === NaN is false!), and it distinguishes 0 from -0. In practice, prefer Number.isNaN() for NaN checks.
// ✅ what you'll write 99% of the time
const role = "admin";
console.log(role === "admin");
 
// ✅ the one idiomatic == : catches null AND undefined
let nickname = null;
console.log(nickname == null);
 
// ✅ NaN never equals itself — test it properly
const result = Number("not a number");
console.log(result === NaN);      // false — always! NaN !== NaN
console.log(Number.isNaN(result)); // true — the correct check

true, true, false, true — that third line is why === NaN can never work: NaN is the only value in the language that doesn't equal itself.

Next lesson those truthy and falsy values start earning their keep: if, else, and teaching your programs to make decisions.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion