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.
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?
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
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!
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.
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....
Weird right? Well get used to it, we’re not even halfway done.
Panel 2 - Arrays Get Coerced Too
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
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(); // ""
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
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…
Panel 3 - Quick Recap
This is true
0 == '0'; // true
Because coercion turns this into 0 == ToNumber("0")
.
This is true
0 == []; // true
Because coercion runs twice:
ToPrimitive([])
gives empty string- Then
ToNumber("")
gives 0.
So then tell me…according to the above rules, what should this return?
'0' == [];
Panel 4 - FALSE!
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
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!