LOOSE EQUALS (==) || STRICT EQUALS (===)

Chinedu Ikechi
Analytics Vidhya
Published in
5 min readMay 5, 2020

--

Photo by Filiberto Santillán on Unsplash

I asked a colleague the difference between the “double equal to” and the “triple equal to” (that’s what we call it), and he looked me in the eye in such a way that would make my girlfriend jealous and said: “The double equal to” checks if the values are equal while the “triple equal to” checks if the types and values are equal”, and was waiting to be handed a trophy if ever there was one for answering the question correctly.

It was a wrong answer.

I did answer the same, till I read YDKJS by Kyle Simpson. An amazing book. I think all JS programmers would benefit a lot from reading it.

Well, back to class.

The correct answer is that loose equals (==) allows coercion in the equality comparison while strict equals (===) disallows it.

Take a little moment to digest it.

In the former (inaccurate answer), it seems that “===” is doing more work than “==” as it has to also check for the type. In the correct answer, the “==” is doing more work because it has to process the coercion if the values are of different types.

It has nothing to do with performance though, “==” is not going to be slower than “===” in any relevant way. If you’re comparing two values of the same types, “==” and “===” use identical algorithm. If you’re comparing two values of different types, the performance isn’t the important factor. The big question should be: do I want coercion in this operation or not?

If you want coercion, use “==”, but if you don’t want coercion, use “===”.

The “==” operator’s behavior is defined as “The Abstract Equality Comparison Algorithm” in section 11.9.3 of the Es5 spec.

The first clause (11.9.3.1) says that if the two values being compared are of the same type, they are just going to be compared based on their identity. I.e. 42 is only equal to 42 and “foo” is only equal to “foo”.

The rest of the algorithm in 11.9.3 specifies that if you use == to compare values of different types, one or both of the values will need to be implicitly coerced. This coercion happens so both of the values eventually end up as the same type, whence they can be evaluated for equality based on value identity.

Comparing: strings to numbers

We’re not surprised that cristiano === ronaldo returned false, because no coercion is allowed.

However, cristiano == ronaldo comparison uses loose equality, which means that if one of the values happens to be of a different type, the comparison algorithm is going to perform implicit coercion on one or both of the values.

You might be wondering what kind of coercion happens between the values. Is the value of cristiano coerced to a string or is the value of ronaldo coerced to a number?

Let’s see what the ES5 spec, clauses 11.9.3.4–5 say:

1. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).

2. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.

From the spec, we can deduce that ronaldo which is a string (“7”) is coerced to a number (7) by the ToNumber abstract operation and then, when the values are compared for equality, it returns true as the resulting values are now equal.

Comparing: anything to Boolean

One of the biggest gotchas with the implicit coercion of loose equality (==) pops up when you try to compare a value directly to true or false.

Consider:

WTF? How come ronaldo which is a “truthy” value, not “==” to true.

gif from giphy

Let’s take a peep into ES5 spec, clauses 11.9.3.6–7:

1. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.

2. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

So, applying the spec in our example code: Type (verdict) is Boolean, so ToNumber(verdict) is performed to coerce its value to become 1 (true == 1).

Now, 1 == “GOAT” is evaluated. The types are still different, so the algorithm is reconsulted (recursively), which will try to coerce “GOAT” to a number which clearly fails: NaN, and 1 == NaN is false.

“GOAT” is indeed truthy, but “GOAT” == true is not performing a Boolean test/coercion at all. “GOAT” is not being coerced to a Boolean (true), but instead, true is being coerced to 1, and then “GOAT” is coerced to NaN.

ToBoolean is not even involved, so the truthiness or falseness of “GOAT” is irrelevant to the == operation. Whenever a Boolean value is on either side of the ==, it always coerces to a number first. Yeah, really strange.

It is recommended to never, under any circumstances, use == true or == false.

Safe Use of Implicit Coercion

Be conscious of the values that can show up on either side of an == comparison. To effectively avoid issues with such comparison, here’s some heuristic rules to follow:

1. If either side of the comparison can have true or false values, don’t ever, ever use ==.

2. If either side of the comparison can have [], “”, or 0 values, seriously consider not using ==.

It’s almost better to use strict equality instead of loose equality, to avoid unwanted coercion. The above rules when followed will pretty much go a long way to prevent all the coercion gotchas that could hurt you.

I hope y’all are staying safe and strong?

--

--

Chinedu Ikechi
Analytics Vidhya

Learn more about JavaScript, databases, microservices, distributed systems, and cloud technologies.