5 Oddities of JavaScript That Might Cause Headaches
Welcome to JavaScript’s world of madness
If you are familiar with JavaScript you have most likely encountered a few unexpected and odd behaviours while working with it. Some of them might make for a good joke between developers, but knowing them can save you a lot of time and trouble debugging your code.
In this article, I want to introduce you to some of these quirks.
1. Equality and Sameness
There are subtle yet important differences in how JavaScript defines equality and sameness. For beginners, the most confusing one is probably the difference between abstract ==
and strict ===
equality checks.
Let’s start by taking a look at a few examples:
How about comparisons with null
?
If you convert null
to a boolean it’s false
.
However, if you try to compare null
to false
the result will still be false
!
What if we compare other falsy values like 0
or ""
to false
though?
You might assume it would be false
then as well. That’s only true for the strict equality check though, the abstract will result in true
!
In the example above we saw that the abstract equality check in some cases classifies two variables of different types as equal. However, if you compare an empty array which is a truthy value to true
you might be surprised again:
It’s important to know the differences between abstract and strict equality comparisons in JavaScript. I highly recommend checking out this article by MDN where they explain equality and sameness in a lot of detail.
2. Math Madness
We continue our journey to mathematics! JavaScript has some beautiful quirks prepared for us here:
Why is the first example correct but the second not? The reason for this is that the first one only works in this specific case. If you take a look at the order in which JavaScript evaluates these expressions it becomes clear why you get these results:
Another pitfall when it comes to math in JavaScript are these “magically” increasing numbers:
Because of the IEEE 754–2008 standard, JavaScript rounds at this scale to the next nearest even number. This is the same for many other programming languages, not just JavaScript. The standard is also responsible for the following problem:
The issue is explained in a lot of detail in this answer on StackOverflow.
It’s also important to understand how JavaScript decides between addition and concatenation. In general, you can say that every time a string is involved in addition it becomes a concatenation at some point in the operation.
However, there are some surprising cases where an addition becomes a whole different type as you can see in the example below:
Hold tight, I saved the most confusing example for the end. Let’s compare the results of the functions Math.min()
and Math.max()
This is definitely not what I expected when I used this function for the first time. Charlie Harvey explained this behaviour on his blog, so take a look there if you are interested in the details.
3. Fun With Arrays
We already saw a few examples with arrays in the previous sections, but there are a lot more interesting facts about arrays.
For example, if you try to add two arrays together, you get the following:
This seems confusing at first, but makes sense when you understand the order in which this concatenation is being performed.
It’s also interesting how JavaScript deals with trailing commas.
In arrays, trailing commas will be ignored as you can see in this example:
But what if you have multiple trailing commas? This is how MDN describes the behaviour of JavaScript for this example:
If more than one trailing comma is used, an elision (or hole) is produced. An array with holes is called sparse (a dense array has no holes). When iterating arrays for example with
Array.prototype.forEach()
orArray.prototype.map()
, array holes are skipped.
You can see that the first two commas generate the previously described holes. However, the last comma will be ignored as it is with normal array items. That’s how we end up with a final length of 5
.
4. Tricky Arrow Functions
Introduced in ES6, arrow functions quickly became popular because of their minimal syntax and more intuitive behaviour of the this
object.
A common pitfall that almost everyone who worked with arrow functions has already encountered is when you return a value in your function:
It’s important to not miss the braces here; otherwise, your function will return undefined
because you opened the function without returning any value.
To learn more about arrow functions in JavaScript, you can take a look at the MDN documentation.
5. Strings Are Not Strings?
Yes, you read that right. Take a look at the following example:
You can see that typeof
works as expected, but instanceof
returns false
for a String
, even though we know that "str"
is indeed a string.
However, if you use the String()
constructor, you will get the expected result:
What is the reason for this? MDN describes instanceof
like this:
The
instanceof
operator tests to see if theprototype
property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.
The reason for this is that the string primitive is not exactly the same as the String object. If you still want to type check the primitive, you should use typeof
instead of instanceof
.
6. Bonus: HTML in JavaScript!
Did you know that you can write a bit of HTML in JavaScript? As you can see in the following example, HTML comments in JavaScript are perfectly valid.
This was introduced a long time ago so older browsers — that didn’t understand the <script>
tag yet — wouldn't crash. These browsers are long dead, but the feature remains. Even in NodeJS!
This was just a tiny selection of many quirks in JavaScript. If you want to dive deeper, I can recommend this awesome GitHub repository by denysdovhan called wtfjs.