JavaScript has two different equality operators that look almost identical but behave very differently: == (loose equality) and === (strict equality). Mixing them up is one of the most common sources of subtle bugs for developers new to the language, so it's worth understanding exactly what each one does.

== Performs Type Coercion

Loose equality (==) will convert the operands to the same type before comparing them, which can produce results that feel surprising:

console.log(5 == "5");     // true  — string "5" is coerced to number 5
console.log(0 == false);    // true  — false is coerced to 0
console.log(null == undefined); // true — special case, these two are considered equal
console.log("" == 0);        // true  — empty string coerced to 0
console.log(" " == 0);       // true  — whitespace string also coerces to 0!

Output:

true
true
true
true
true

=== Requires Matching Types

Strict equality (===) never coerces — both the value AND the type must match:

console.log(5 === "5");     // false — different types (number vs string)
console.log(5 === 5);        // true  — same type, same value
console.log(0 === false);    // false — number vs boolean
console.log(null === undefined); // false — strict equality treats them as different

Output:

false
true
false
false

Why === Is the Recommended Default

Because =='s coercion rules are genuinely hard to memorize in full (even experienced developers get edge cases wrong), the near-universal convention in modern JavaScript is to use === and !== everywhere, and only reach for == in the rare case where you deliberately want null and undefined to be treated as equal:

function processValue(value) {
  // Deliberately using == here to catch BOTH null and undefined in one check
  if (value == null) {
    return "No value provided";
  }
  return `Processing: ${value}`;
}

console.log(processValue(undefined));
console.log(processValue(null));
console.log(processValue(0));

Output:

No value provided
No value provided
Processing: 0

Notice processValue(0) correctly falls through to the "Processing" branch — if that check had used if (!value) instead, it would have wrongly treated 0 as "no value," which ties directly into how JavaScript coerces values in general. As a rule of thumb: default to ===, and you'll avoid the vast majority of equality-related bugs.