== 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.
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; // trueThat 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 hereLook 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; // falseThis 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 NaNNote 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]; // falseNeither 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 namesIf 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 == nullis the one loose comparison worth knowing, and it catchesnullandundefinedtogether.NaNis equal to nothing, including itself — useNumber.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
The frontend portfolio that actually gets replies
Most junior portfolios are three clones of the same todo app. Here is what hiring managers look for instead, and how to build it in a few weekends.
Why tutorials don't stick, and what to do instead
You can follow along with a four-hour video and still be unable to write the code yourself. Here is what is actually going wrong, and the small change that fixes it.