TLDR: Coerce yourself to use triple equals.

I unintentionally found this JavaScript meme on Reddit, and it's the best one I've ever seen.

best-js-meme-to-date-2

You can verify this meme's accuracy by running each code snippet in Developer Tools. The result isn't surprising, but still kind of disappointing.

Of course this little experiment lead me to wonder...

Why Does This Happen?

why-does-this-happen

With experience, I've learned to embrace JavaScript's smooth sides while bewaring its prickly pines. Nonetheless, this corner case's details still nicked me.

It's just as Kyle Simpson says...

"I don’t think anyone ever really knows JS, not completely anyway."

When these cases pop up, it's best to consult the source–the official ECMAScript specification that JavaScript is built from.

With the spec in hand, let's deeply understand what's going on here.

Panel 1 - Introducing Coercion

panel-1-1

If you run 0 == "0" in your developer console, why does it return true?

0 is a number, and "0" is a string, they should never be the same! Most programming languages respect that. 0 == "0" in Java, for example, returns this:

error: incomparable types: int and String

This makes perfect sense. If you want to compare an int and String in Java, you must first convert them to the same type.

But this is JavaScript, y'all!
this-is-javascript

When you compare two values via ==, one of the values may undergo coercion.

Coercion–Automatically changing a value from one type to another.

Automatically is the key word here. Instead of you explicitly converting your types, JavaScript does it for you behind the scenes.

scumbag-javascript

This is convenient if you're purposely exploiting it, but potentially harmful if you're unaware of its implications.

Here's the official ECMAScript Language Specification on that. I'll paraphrase the relevant part:

If x is Number and y is String, return x == ToNumber(y)

So for our case of 0 == "0":

Since 0 is Number and "0" is String, return 0 == ToNumber("0")

Our string "0" has been secretly converted to 0, and now we have a match!

0 == "0" // true
// The second 0 became a number!
// so 0 equals 0 is true....

that-string-secretly-became-a-number

Weird right? Well get used to it, we're not even halfway done.

Panel 2 - Arrays Get Coerced Too

panel-2

This nonsense isn't limited to primitives like strings, numbers, or booleans. Here's our next comparison:

0 == [] // true
// What happened...?

Coercion again! I'll paraphrase the spec's relevant part:

If x is String or Number and y is Object, return x == ToPrimitive(y)

Three things here:

1. Yes, arrays are objects

arrays-are-objects

Sorry to break it you.

2. Empty array becomes empty string

Again according to the spec, JS first looks for an object's toString method to coerce it.

In the case of arrays, toString joins all of its elements and returns them as a string.

[1, 2, 3].toString() // "1,2,3"
['hello', 'world'].toString() // "hello,world"

Since our array's empty, we have nothing to join! Therefore...

[].toString() // ""

empty-array-coerces-to-empty-string-1

The spec's ToPrimitive turns this empty array into an empty string. References are here and here for your convenience (or confusion).

3. Empty string then becomes 0

empty-strings-become-0

You can't make this stuff up. Now that we've coerced the array to "", we're back to the first algorithm...

If x is Number and y is String, return x == ToNumber(y)

So for 0 == ""

Since 0 is Number and "" is String, return 0 == ToNumber("")

ToNumber("") returns 0.

Therefore, 0 == 0 once again...

coercion-every-time-2

Panel 3 - Quick Recap

panel-3-1

This is true

0 == "0" // true

Because coercion turns this into 0 == ToNumber("0").

This is true

0 == [] // true

Because coercion runs twice:

  1. ToPrimitive([]) gives empty string
  2. Then ToNumber("") gives 0.

So then tell me...according to the above rules, what should this return?

"0" == []

Panel 4 - FALSE!

panel-4-1

FALSE! Correct.

This part makes sense if you understood the rules.

Here's our comparison:

"0" == [] // false

Referencing the spec once again:

If x is String or Number and y is Object, return x == ToPrimitive(y)

That means...

Since "0" is String and [] is Object, return x == ToPrimitive([])

ToPrimitive([]) returns empty string. The comparison has now become

"0" == ""

"0" and "" are both strings, so JavaScript says no more coercion needed. This is why we get false.

Conclusion

just-use-triple-equals

Use triple equals and sleep soundly at night.

0 === "0" // false
0 === [] // false
"0" === [] // false

It avoids coercion entirely, so I guess it's more efficient too!

But the performance increase is almost meaningless. The real win is the increased confidence you'll have in your code, making that extra keystroke totally worth it.

Thanks for reading

For more content like this, check out https://yazeedb.com. And please let me know what else you'd like to see! My DMs are open on Twitter.

Until next time!