== versus ===, the famous one
A student types their age into a form. You want to check whether they are 18. You have two equality operators available, they look almost the same, and they give opposite answers.
let typedAge = "18";
console.log(typedAge == 18);
console.log(typedAge === 18);
true false
Neither one is broken. They are asking different questions, and knowing which question you actually meant is the difference between a form that works and a form that lets a fifteen-year-old through.
=== asks one question, == asks two
=== is strict equality. It asks: are these the same type, and the same
value? If the types differ, the answer is false immediately — no conversion,
no negotiation.
console.log(75 === 75);
console.log("Karachi" === "Karachi");
console.log(true === true);
console.log(75 === "75");
console.log(75 !== "75");
true true true false true
!== is the opposite: are these different in type or value?
== is loose equality. If the two sides are already the same type it
behaves exactly like ===. If they are not, it converts one side to match the
other and then compares. That conversion is called type coercion, and it
follows rules nobody has memorised on purpose.
console.log("7" == 7);
console.log(0 == false);
console.log(1 == true);
console.log("" == 0);
console.log(null == undefined);
console.log(null == 0);
true true true true true false
Look at the last two together. null is loosely equal to undefined, which is
reasonable — both mean nothing, as
the data types lesson describes. But null is
not loosely equal to 0, even though false is. There is no principle you
can derive that from. It is a list of special cases in the specification.
The comparison operators
>, <, >= and <= behave the way school algebra taught you, and each
returns a boolean.
let busSeats = 42;
console.log(busSeats > 40);
console.log(busSeats >= 42);
console.log(busSeats < 42);
console.log(busSeats <= 42);
true true false true
Unlike ===, these four have no strict version. They always coerce. That is
worth remembering, because it is where the next trap lives.
Comparing strings is not comparing numbers
console.log("10" > "9");
console.log(10 > 9);
console.log("10" > 9);
console.log("Ayesha" < "Bilal");
console.log("apple" < "Apple");
false true true true false
The first line is the one to stare at. With strings on both sides, JavaScript
compares them character by character, like a dictionary. "1" comes before
"9", so "10" sorts before "9" and the comparison is false.
The third line has one number, so both sides become numbers and you get the arithmetic answer. Same operator, same-looking values, opposite results, decided entirely by whether you remembered to strip the quotes.
The last line shows that sorting is by character code, not by the alphabet as
people think of it. Every capital letter comes before every small one, which
is why "apple" < "Apple" is false. Lowercase both sides before comparing
names, using the method from
the strings lesson.
Why beginners reach for ==, and what it costs
The honest reason == gets used is that it is one character shorter, it works
in most of the cases a beginner tries, and typing three equals signs feels
like superstition. So you write ==, everything passes, and you conclude the
strict version is pedantry.
Then a real value arrives. Something is 0 when you expected false, or an
empty string when you expected a missing value, and == cheerfully declares
two unrelated things equal. Now you are debugging a comparison that looks
correct on the screen.
The specification's conversion rules run to several pages. They involve
converting objects to primitives, then strings to numbers, with different
orders depending on which types are involved. Experienced developers do not
have them memorised either. They just do not use ==, so they never need to.
The rule is short: use === and !== everywhere. When the two sides are
different types, that is a fact you want to know about, not one you want
silently smoothed over. Convert deliberately with Number or String, then
compare strictly, and every comparison in your program means what it says.
The famous one: [] == false
The example that gets shown whenever people argue about JavaScript uses an empty array — a list with nothing in it, which you will meet properly in the arrays lesson.
console.log([] == false);
console.log([] == "");
console.log([] == 0);
console.log([] == "0");
true true true false
An empty array is equal to false, equal to an empty string, equal to zero,
and not equal to "0". Traced through the rules it is consistent: the array
is converted to a primitive, which gives the empty string ""; false
becomes the number 0; "" also becomes 0; and 0 == 0 is true. The last
line stays a string-to-string comparison, and "" is not "0".
Every step is legal. The result is still absurd. That is the point — the
coercion rules do not produce nonsense because they are broken, they produce
nonsense because they are applied faithfully to values that should never have
been compared in the first place. === refuses at the first step, which is
the behaviour you want.
The one place == earns its keep
There is a single idiom where experienced developers use == deliberately.
x == null is true for exactly two values — null and undefined — and
false for everything else.
let middleName = null;
let nickname;
console.log(middleName == null);
console.log(nickname == null);
console.log(0 == null);
console.log("" == null);
true true false false
That is genuinely useful for "is there no value here, by either route?" without
writing the check twice. It is safe because that particular rule is narrow and
easy to remember. It is also the only exception, and if you would rather write
middleName === null || middleName === undefined, nobody will complain.
NaN is not equal to anything, including itself
console.log(NaN == NaN);
console.log(NaN === NaN);
false false
Neither operator helps here, so no amount of switching between them will
detect a failed calculation. Number.isNaN is the tool, and
the numbers lesson covers why.
Convert first, then compare strictly
The pattern that makes the whole problem disappear: turn the incoming text into
the type you actually mean, in a named variable, then compare with ===.
let enteredMarks = "88";
let marks = Number(enteredMarks);
console.log(marks === 88);
console.log(enteredMarks === 88);
console.log(marks > 80);
true false true
Two lines instead of one, and the conversion is visible to anyone reading the code — including you in six months. Every comparison after that line is comparing numbers to numbers, which is the only kind of comparison that never surprises you. Those booleans are about to become useful in the if and else lesson.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes