I recently stumbled on a tweet about some of the things considered weird in Javascript. I decided to write this post to share some of the reasons why this things are in javascript.
https://twitter.com/Ashot_/status/1287818215465324546?s=20
Understanding why they are, will lead to less confusion about the way the language handles operations across several data types. Some of these weirdness happen because Javascript is a loosely typed language, which isn't bad in itself but it just means you should be careful the way you use operators and not to always assume.
-
Why is the
typeof NaN === 'number'
?NaN
stands forNot a Number
but suprisingly has a type,number
.This is one thing I considered weird when I started writing Javascript.
I understood why that is after reading an article on Ire Aderinokun's blog, I quote from her blog:
The type of
NaN
, which stands for Not a Number is, surprisingly, a number. The reason for this is, in computing,NaN
is actually technically a numeric data type. However, it is a numeric data type whose value cannot be represented using actual numbers. So, the name "Not a Number", doesn't mean that the value is not numeric. It instead means that the value cannot be expressed with numbers. -
Why is 9999999999999999 converted to 10000000000000000 in JavaScript?
Javascript doesn't have integers. 9999999999999999 is treated internally as a floating point number and at this point has ran out of floating-point precision.
Someone explained it beautifully on StackOverflow here.
-
0.1 + 0.2 !== 0.3
I remember having a chat with Michael about this some months back. I was literally pulling my hair and cursing because it didn't make any sense to me. I decided to research more about it and I found a really helpful explanation here.
A number is stored in memory in its binary form, a sequence of bits – ones and zeroes. But fractions like
0.1
,0.2
that look simple in the decimal numeric system are actually unending fractions in their binary form.In other words, what is
0.1
? It is one divided by ten1/10
, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third:1/3
. It becomes an endless fraction0.33333(3)
.So, division by powers
10
is guaranteed to work well in the decimal system, but division by3
is not. For the same reason, in the binary numeral system, the division by powers of2
is guaranteed to work, but1/10
becomes an endless binary fraction.There’s just no way to store exactly 0.1 or exactly 0.2 using the binary system, just like there is no way to store one-third as a decimal fraction.
The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don’t allow us to see that “tiny precision loss”, but it exists.
Understanding the floating point arithmetic is very crucial to understanding why this is.
console.log((0.1).toFixed(20)); // 0.10000000000000000555 // And when we sum two numbers, their “precision losses” add up. // That’s why 0.1 + 0.2 is not exactly 0.3. console.log(0.1 + 0.3); // 0.30000000000000004
-
Math.max() === -Infiinity and Math.min() === Infinity
Weird right?
The question I asked myself when I saw this is, what exactly does Math.max() do?
I initially confused it with
Number.MAX_VALUE
which represents the maximum numeric value representable in JavaScript. Values greater thanNumber.MAX_VALUE
are represented as Infinity.Infinity > Number.MAX_VALUE; // true
Math.max
is a function that returns the largest of the zero or more numbers given as input parameters, when we callMath.max()
we pass in no arguments.Math.max(1, 4, 2); // 4 Math.min(2, 1, 3); // 1 Math.min(1); // 1
When we pass in just
1
into theMath.min
function, the function returns1
because that's the sole value passed into the function. Internally, the function needs something to compare values passed into it against, like a starting value, let's call this anIdentity
variable.These articles might help make things clearer:
-
[] + [] == ""
Adding two arrays returns an empty string, this happens because the
+
operator only exists for strings and numbersin javascript. When you try to add two arrays, Javascript tries to convert the array into a string by extracting the content of the array and converting it to a string, if the array is empty then it defaults to an empty string, hence why[] + [] == ""
.[] + []['bol'] + // "" ['aji'][('bo', 'la')] + // 'bolaji' ['ji']; // bo,laji
Javascript is basically calling the
toString
method on the array prototype and concatenating the result. -
[] + === '[object Object]'
From our understanding of the way the addition operator works so far we know the operator works mainly with strings and numbers. We can convert an array to a string using the
toString
method that exists on the Array prototype.var arr = ['bol', 'aji']; console.log(arr.toString()); // 'bol,aji' // we can also coerce it into a string using the `String` method in javscript console.log(String(arr)); // 'bol,aji'
However, when we try to coerce an object the result is
'[object Object]'
and that's why[] + {}
is equal to'[object Object]'
.var arr1 = []; var arr2 = ['proton']; var obj = {}; console.log(arr1 + obj); // '[object Object]' console.log(arr2 + obj); // 'proton[object Object]'
-
+ [] === 0
Since coercing objects and arrays to their string equivalent is what happens when we use the addition operator on them, it's weird that adding an array to an object returns a value of 0 which is a number. This is because the curly brace "{" is a very important character in Javascript used to denote the beginning of a statement block.
When a statement begins with "{" in Javascript, it's usually interpreted as a block. Since the statement begins with an empty block, the interpreter assumes nothing is happening and goes on to evaluate
+ []
, because the statement now starts with theaddition
operator, it coerces the array into a number type.Number([]); // 0 Number(['proton']); // NaN
Check out this StackOverflow answer.
-
Booleans are evil numbers?
true + true + true; // 3 true - true; // 0 true == 1; // true true === 1; // false
The addition operator
+
used here forces Javascript to coerce the boolean values into numbers because+
is primarily meant for numbers.Number(false); // 0 Number(true); // 1 true + true + true; // 1 + 1 + 1 = 3 true - true; // 1 - 1 = 0
The
==
operators compares two values regardless of their type - since the numerical representation oftrue
is1
that's whytrue == 1
returns true. However, the===
compares the types of the two values and that's whytrue === 1
returns false. -
[] == 0 returns true
This is also because of the way the
==
operator works.The == version of equality is quite liberal. Values may be considered equal even if they are different types, since the operator will force coercion of one or both operators into a single type (usually a number) before performing a comparison. - Source
According to ECMA, here's the way the
==
operator works whenx == y.
According to the image above, when comparing
[] == 0
, we'd need to convert the array[]
into it's primitive type, I don't want to dwell too much on primitives because it's a big topic on it's own but if you go through this post here you will be able to figure that the primitive type of an array is the value of.toString
, which in our case is an empty string.At this point, we've been able to reduce the comparison to
"" == 0
, according to the spec above, when you compare a string and a number with the==
operator, the result will be the result of coercing the string into a number and comparing the two numbers.[] == 0; // when you convert the empty array to it's primitive type using [].toString() // the result is an empty string Number(''); // 0 0 == 0; // true
-
9 + "1" = "91' but 91 - "1" = 90
This is absolutely normal (for a loosely-typed language, obviously) because the addition operator can be used on strings while the subtraction operator can't.
When you add a string to a number, Javascript coerces the number into a string, then concatenates the both of them.
9 + '1'; // Javascript coerces the number into a string '9' + '1'; // then after normal string concatenation we get ('91');
However, because subtraction isn't a valid operator for strings, Javascript coerces the string into it's number equivalent.
// since subtraction isn't a valid operator for strings 91 - '1'; // Javascript coerces the string into a number 91 - 1; // the result of the subtraction between the two numbers return a number 90;
If this was helpful, feel free to share and/or drop a comment.
If you’ve got questions, feel free to share them in the comment section or reach out to me on twitter.
You may also want to check out this post by Toptal about the most common mistakes that many JavaScript developers make.