Introduction
Everyone loves to hate on JavaScript == operator. Some people will tell you it’s all overblown. I'm here to tell you why it's not and prove that the == operator is bad, actually. I know, very brave.
The big issue is that == performs type conversion, and so do operators like + and *. That means JS will automatically convert data of one type to another in certain circumstances. For example:
'2' * '3' === 6 |
You can see that JS automatically converted the types so that all these operations make sense. Normally, you can’t multiply two strings. So, JS converted them to numbers and then multiplied them. The opposite happened for addition, where it converted the number to a string.
The conversion itself is not necessarily an issue. The unpredictability is. The conversion rules are not completely arbitrary, but they are complex and unfamiliar, and most developers would rather spend their time developing than memorizing them. To illustrate this point, here are a few examples of expressions that are true in JavaScript.
1 == '1' ![0] == false |
Background/Problem
As you can see with just a few examples, there are a lot of unexpected results for such a seemingly simple operator. Since you don’t specify types in JS, it is very easy to accidentally fall into a == trap. Sure, 1 == ‘1’ makes enough sense, but [1] == ‘1’? [] == 0? What’s going on here? Let’s find out.
We’ll go through the examples grouped by behavior. (Note: every time == or != is used in this post they return true unless otherwise indicated. Remember that.) Here is the first group:
1 == '1' |
What's up with these? Well, it would help to know the rules in JS for comparing numbers to strings, since that’s what’s happening in all of these examples. The first example, 1 == '1', is pretty obvious. Clearly, 1 is converted to '1' or the other way around, so they're equal. But what about the next two?
A reasonable guess is that the string is converted to a number, and then the numbers are compared. If we assume that the number conversion is pretty generous, then these comparisons all make sense. Let's try that.
0 == '' // true |
Huh? Well, what does parseInt return when it can't parse the string? NaN (Not a Number). NaN is not equal to anything, including itself. parseInt on the empty string is NaN. The "canonical" way to convert a string to a number is actually to use the Number constructor (see here), which ignores leading and trailing whitespace, and returns NaN if the string is not a valid number.
0 == '' |
How about a few more cases?
[1] == '1' [1,2] != [1,2] |
In JavaScript, when comparing arrays to primitives the array’s elements are converted to strings by joining them with commas. So the first three cases above become
'1' == '1' |
The last case is different. [1,2] != [1,2] because the arrays are objects. Two objects are compared as equal only if they are the same object. This is true for all objects, not just arrays. The arrays in this example are two separate objects, they just happen to have the same content. Here are some more examples to illustrate this point:
x = [1,2] |
Regular objects work the same way:
z = {} |
Speaking of objects, how about null and undefined? Null and undefined mean different things, and are actually different types. undefined means a variable does not have a value whereas null means a variable has no value. You can read more about that here. Here are a few examples of how they compare to each other and other JS values:
null == undefined |
Useful feature alert!!! Using ==, null and undefined are not equal to anything except each other. This is occasionally useful and is easy to reason about.
function do_something(x) { |
This is pretty much the only usable “feature” of == in JavaScript. Even then, you have to be confident other developers know this behavior and there are often better ways to do what you want.
Now let's move on from the simple null/undefined case and get a little weird.
[] == 0 |
From the top:
Arrays can behave really unintuitively because an empty array is truthy, but empty arrays are sometimes converted to empty strings, which are falsy. As usual, all of the following are true:
[] == ![] |
Lightning round!
Now things are starting to make sense!
So what happens when the arrays aren’t empty? Here are some examples of that:
[0] == 0 |
These array comparisons all start with converting the array to a string:
[0] == 0 |
Then, the string is converted to a number:
'0' == 0 |
For the string comparisons, the conversion to a string is all that happens.
[0] == '0' |
Time for a whole bunch more:
[0] != [0] [0] == false ![0] == false [[1],[[2]]] == '1,2' |
There's actually not much new here. Let's go over them quickly
If you can understand all that, then you’re golden. You may even be ready for the final example:
''*false+0-'b' != ''*false+0-'b' |
This one's weird to look at, but under the hood, it's pretty simple. The left and right-hand side of both equations are the same, and they're not objects, so it's weird that one is != but the other is ==. Here’s the explanation:
Benefits/Impact
You'll notice that many of these comparisons go through multiple levels of conversion to get to the end result. This is part of what makes the == operator difficult to reason about. The == operator also hides intent. Maybe you, the developer, knew that you were intentionally comparing [] to coerce to '' to coerce to 0 to get true, but someone looking at your code might not. It's better to be explicit about your intent and use === instead. That’s the “strict equality” operator. === does not coerce types, so it's much easier to reason about.
So, what are the rules? Here's a summary, you can read about them in more detail on MDN:
Well, that's actually not so bad! Those rules really aren't that complicated, but they are complicated enough that it's not worth memorizing them. Especially since step 3 (convert objects to primitives) holds a lot of hidden complexity. Maybe if everyone you worked with knew the rules it would be worth it for you to know them too; but they don't, and it isn't. It's better to just use === and !== in almost all cases and forget about the terrors of ==.
Solution
If this operator is so terrible, what is there to do? Well, use === of course. But that’s not the end of the story. You should definitely be using code quality tools in your codebase, and a linter will be happy to catch the use of == along with many other bad code patterns. In ESLint, it is the eqeqeq rule. TypeScript will also warn you if you’re trying to compare two types that you probably shouldn’t. The equality operators look pretty similar, so it’s easy to miss them in a code review. But most importantly, at the end of the day, it isn’t really that big of a deal.
In most cases using == isn’t catastrophic. It’s not likely to bring down your entire site or result in a company-destroying security vulnerability. You’re probably using it to compare things that are the same type anyways. Just know that if you aren’t, or you think you are but you’re wrong about the types, it can result in some seriously unexpected behavior.
Conclusion
The == operator is fraught with potential errors. It was designed to be easy to use, so you could do cool things like 1 == ‘1’. Unfortunately, that made it hard to use because there are a lot of cases where that intuition doesn’t work. Even if your intuition is correct, it’s often still wrong. The reason [] == false isn’t really because they are similar, but because [] as a string is '' which as a number is 0, and false as a number is also 0.
Not every idea pans out, and JavaScript arguably has more than its fair share of bad ideas, but that's okay. Through working with JS, I’ve found it’s adopted a lot of good ideas too. At least the === operator exists, and you can use that. Unfortunately, it’s easy to miss == vs === in a code review, but it’s not the end of the world. Just add a lint rule or use TypeScript.
Author Bio
Ethan Ferguson is an associate software engineer at Jahnel Group, Inc., a custom software development firm based in Schenectady, NY. At Jahnel Group, we're passionate about building amazing software that drives businesses forward. We're not just a company - we're a community of rockstar developers who love what we do. From the moment you walk through our door, you'll feel like part of the family. To learn more about Jahnel Group's services, visit jahnelgroup.com or contact Jon Keller at jkeller@jahnelgroup.com