AniUI Academy

== versus ===, settled properly

The rule everyone repeats is "always use triple equals". That is good advice and a bad explanation. Here is what each one actually does.

Anish Lawrence7 min readJavaScriptFundamentals

Every JavaScript style guide says the same thing: use ===, never ==. The advice is correct. The trouble is that repeating a rule you cannot explain leaves you helpless the moment you meet code that breaks it.

So let us do the explanation properly.

=== compares without converting

Strict equality asks one question: are these the same type, and are they the same value? If the types differ, the answer is false and nothing else happens.

1 === 1;        // true
"1" === 1;      // false — string and number
null === null;  // true

That is the whole rule. There is no second step.

== converts first, then compares

Loose equality tries to be helpful. If the two sides are different types, it converts one of them and tries again. The conversion rules are specific, several steps long, and almost nobody has them memorised.

"1" == 1;        // true  — the string becomes a number
0 == false;      // true  — the boolean becomes a number
"" == 0;         // true  — the empty string becomes 0
null == undefined; // true — a special case, hard-coded
null == 0;       // false — null converts to nothing useful here

Look at that last pair. null == undefined is true, but null == 0 is false, even though undefined and 0 are both falsy. That is not a rule you can derive from intuition. It is a special case written into the specification.

The one case where == earns its keep

There is exactly one idiom worth knowing:

if (value == null) {
  // runs for null AND undefined, nothing else
}

Because null == undefined is hard-coded as true and null is loosely equal to nothing else, this is a compact way to catch both "no value" cases at once. Some codebases use it deliberately. The strict equivalent spells it out:

if (value === null || value === undefined) {
}

Both are fine. Pick whichever your team reads more easily, and be consistent.

NaN, the value that is not equal to itself

NaN === NaN; // false
NaN == NaN;  // false

This is not a bug. NaN means "the result of a calculation that has no numeric answer", and two separate failed calculations are not meaningfully the same. Since neither operator helps you, use the purpose-built check:

Number.isNaN(value); // true only for NaN

Note Number.isNaN, not the old global isNaN. The global one converts its argument first, so isNaN("hello") is true — which is almost never what you meant.

Objects compare by identity

{ a: 1 } === { a: 1 }; // false
[1, 2] === [1, 2];     // false

Neither operator looks inside. Both objects have the same contents, but they are two different objects, and equality asks whether the two references point at the same one. They do not.

const first = { a: 1 };
const second = first;
first === second; // true — same object, two names

If you want to compare contents you have to write that comparison yourself, or reach for a library.

What to remember

  • === compares type and value, with no conversion. Reach for it by default.
  • == converts first, using rules that are not worth memorising.
  • value == null is the one loose comparison worth knowing, and it catches null and undefined together.
  • NaN is equal to nothing, including itself — use Number.isNaN.
  • Objects and arrays compare by identity, never by contents.

"Always use ===" is still the right default. Now you know what you are defaulting away from.

Related reading