As one of the most popular programming languages in the world, JavaScript is widely used for building dynamic and interactive websites, web applications and even desktop and mobile applications. JavaScript is a gateway to other web technologies, because of its widespread use and compatibility with various platforms. It has become a foundational language in modern web development. It is also a complex language with many unexpected moments that can leave even the most experienced developers scratching their heads.
In this article, we'll see various unexpected JavaScript moments that can confuse, test your limits and frustrate you. It will also help you to change the way you code.
Are you ready to make your brain explode🤯? If so, let's enter the jungle..
1️⃣
2 == [2] // true
==
operator in JS performs type coercion which means it tries to convert the values being compared to a common data type before making the comparison.
In this instance, the number 2 is converted to a string and the array [2]
is converted to a string as well. Resulting in both values being 2
. That's why, the comparison evaluates to true
.
It's generally recommended to use the strict equality operator ===
instead of ==
to avoid unexpected results due to type coercion..
Explore more:
'123' == 123 // true
'foo' == NaN // false
undefined == null // true
NaN === NaN // false
NaN == NaN // false
0 == null // false
2️⃣
[] == ![] // true
Comparing an empty array []
with a boolean
value created by negating (using the !
operator) a non empty array []
. The result of this comparison is true
, which might seem unexpected at first glance..
In JS, every value can be either true
or false
in a boolean
context. An empty array is a truthy value, which means it's considered true
in a boolean
context. When we apply the !
operator to it, it's converted to false
.
On the other side, the boolean
value created by negating a non empty array is false
. When we compare an empty array (truthy) with a false
value (falsy) using the ==
operator, JS performs type constraint which means it tries to convert the values to a common type before comparing them. So, the empty array is converted to false
which results in both sides being false
. At the end, the comparison returns true
.
Explore more:
['a', 'b'] !== ['a', 'b'] // true
['a', 'b'] == ['a', 'b'] // false
[1, 2] + [3, 4] // "1,23,4"
3️⃣
null == undefined // true
The double equals ==
operator is used to compare two values for equality, while ignoring their data type. When comparing the values null
and undefined
using the double equals operator they are considered equal and the result of the comparison will be true
. This is because both null
and undefined
represent a lack of a value and are so equivalent to each other in this context.
With strict equality operator:
null === undefined // false
4️⃣
typeof NaN // number
typeof null // object
In JS, typeof is an operator used to determine the type of a value or variable.
NaN stands for Not a Number and is a special value in JS that represents an undefined
or unrepresentable
numerical value.
When you use typeof
with NaN
, it will return number
. This might seem strange but it's because NaN
is technically a numeric data type in JS even though it represents something that isn't actually a number.
When typeof
is applied to null
, it returns the string object
. This is because null
is considered to be a special value that represents an empty object reference. null
is not an object itself but rather a primitive value. This is considered to be a quirk or oddity in the language design of JS.
Explore more:
typeof function(){} // "function"
null instanceof Object // false
5️⃣
true == "1" // true
false == "0" // true
JS converts the string 1
to the boolean
value true
and string 0
to false
because any non empty string is considered truthy and on other side falsy. So, the comparison becomes true == true
which is true
and false == false
which is true
.
Explore more:
1 + true // 2
1 - true // 0
'' == false // true
0 == false // true
true + false // 1
6️⃣
"1" + 1 // "11"
2 + "2" // "22"
"5" - 3 // 2
When you use the + operator with a string
and a number
, the number is converted to a string and concatenated to the string.
If the string
can be parsed as a number
, it will subtract the number
from the string
.
So,
"1" + 1
becomes the string "11"
2 + "2"
becomes the string "22"
"5" - 3
becomes the number 2
Explore more:
+"1" // 1
-"1" // -1
+true // 1
-true // -1
+false // 0
-false // -0
+null // 0
+undefined // NaN
1 / "2" // 0.5
"2" / 1 // 2
1 / 0 // Infinity
-1 / 0 // -Infinity
3 * "abc" // NaN
true > false // true
undefined + 1 // NaN
undefined - 1 // NaN
undefined - undefined // NaN
undefined + undefined // NaN
null + 1 // 1
null - 1 // -1
null - null // 0
null + null // 0
Infinity + 1 // Infinity
Infinity - 1 // Infinity
Infinity - Infinity // NaN
Infinity + Infinity // Infinity
Infinity / Infinity // NaN
7️⃣
"b" + "a" + + "a" + "a" // "baNaNa"
It concatenates the string b
, the string a
, the string resulting from the expression +"a"
and the string a
.
+"a"
force the string a
into a number which evaluates to NaN
(Not a Number) because a
is not a valid number.
When we concatenate b
, a
, NaN
(represented as an empty string) and a
, we get the string baNaNa
.
8️⃣
!{} // false
{} == !{} // false
{} == {} // false
When, we are comparing an empty object {}
to a negated empty object !{}
. The exclamation mark !
is a logical operator that negates the value of the object so !{}
returns false
since an object is considered truthy in JS. We are actually comparing {}
to false
which results in a false
value since they are not equal in value or data type.
In the last expression, we are comparing two empty objects {}
. Despite the fact that they may appear to be identical, they are two separate objects with distinct references in memory so they are not equal in value or data type. In the end the comparison also results in a false
value.
When you use the plus operator + between two objects wrapped in curly braces {}
, it tries to concatenate the objects as strings.
Explore more:
{} + [] === "" // false
!!{} // true
!![] // true
[] + [] // ""
[] + {} // "[object Object]"
{} + [] // "[object Object]"
{} + {} // "[object Object][object Object]"
[] == false // true
!!'' // false
!!0 // false
!!null // false
!!undefined // false
9️⃣
7 > 6 > 5 // false
First, 7 > 6
evaluates to true
because 7 is greater than 6.
Next, true > 5
is evaluated. In JS, true
is force into the number 1
and false
is force into 0
. So 1 > 5
is false
, since 1
is not greater than 5
.
So at the end, 7 > 6 > 5
is equivalent to true > 5
which is false
.
Explore more:
5 < 6 < 7 // true
0 > null // false
1️⃣0️⃣
Math.max() // -Infinity
Math.min() // Infinity
Math.max()
& Math.min()
are functions that can be used to find the largest and smallest values in a set of numbers respectively.
When called without any arguments, Math.max()
returns -Infinity
which represents the smallest possible number
in JS, on other side, Math.min()
returns Infinity
which represents the largest possible number
in JS.
This behavior makes sense because if there are no numbers provided, there is no largest number to return for Math.max()
and following same way, there is no smallest number to return for Math.min()
1️⃣1️⃣
parseInt('08') // 8
parseInt('08', 10) // 8
parseInt('0x10') // 16
parseInt('08')
converts the string 08
into the integer number 8
. If you were to write parseInt('08', 10)
, the function will still return 8
.
The reason behind this is because the second parameter of parseInt
function is the radix which specifies the numbering system to be used. Let's say: binary
, octal
, decimal
, hexadecimal
etc.. If the radix is not specified parseInt
will try to detect the radix based on the string format. In above case, 08
is considered an octal number because it starts with 0
so it gets converted into 8
as a decimal number.
parseInt('0x10')
converts the hexadecimal
string 0x10
into the integer number 16
. The radix is not specified either, but the prefix 0x
indicates that the number should be treated as a hexadecimal
number so it gets converted into 16
as a decimal number.
Explore more:
parseFloat('3.14.15') // 3.14
parseFloat('0.0') // 0
1️⃣2️⃣
(function(x) { delete x; return x; })(1); // 1
An anonymous function that takes an argument x
. Inside the function it tries to delete the x
variable which is not possible because x
is a function argument and cannot be deleted. The function then returns the value of x
.
When this function is called with the argument 1
, the value of x
inside the function is set to 1
. In this case, the delete operation has no effect, the function simply returns the value of x
which is 1
1️⃣3️⃣
@tohodo from the DEV Community had a great point to add to this article. Let's see..
for (var i = 0; i < 3; ++i) {
setTimeout(() => console.log(i), 1000); // returns 3 three times
}
for (let i = 0; i < 3; ++i) {
setTimeout(() => console.log(i), 1000); // returns 0 1 2
}
This is because var
creates a single binding at the function scope, so after a one-second timeout the loop has already run its course, hence you get 3
three times. By using let
you bind the variable at the block scope (loop), so it returns the values you expected since i
refers to the value at that loop iteration.
Thanks to @tohodo for sharing this insight.
Great job exploring and understanding these JavaScript concepts!!! This knowledge will definitely help you in interviews and enable you to assess the skills of potential candidates as an interviewer. Keep up the good work and continue learning something Ctrl+N
❤ Motivation:
🍀Support
Please consider following and supporting us by subscribing to our channel. Your support is greatly appreciated and will help us continue creating content for you to enjoy. Thank you in advance for your support!
Top comments (47)
Good stuff. Another common gotcha:
This is because
var
creates a single binding at the function scope, so after a one-second timeout the loop has already run its course, hence you get3
three times. By usinglet
you bind the variable at the block scope (loop), so it returns the values you expected sincei
refers to the value at that loop iteration.If someone wants to overcome this while using
var
, not many people know thatsetTimeout
can accept params to be passed to the callback, great alternative to bind.Good tip. I used this pattern a lot before ES6.
Good fix
I gave your suggestion a space, here it's..
Thank you again.. Happy reading!!!
this is insightful.
Yes
Yes perfect example. I will add it to the article..
Personally, I don’t use ‘var’. I always use ‘let’ and ‘const’ to avoid these kinds of bugs..
Thank you for your valuable feedback
This was really informative and solved one of most annoying glitch of my small project. Thanks
You're welcome my friend. Glad you found it helpful and thank you for your feedback..
The moral of this article is
1) don't try to be clever. Just write clean, clear expressive code.
2) Using Typescript will avoid the majority of the accidental type coercion errors.
3) please don't rely on these coercion oddities for logic gates. Be explicit.
I appreciate your input on this article
Interesting... But most of this begins to make more sense if you understand how loose typing works through coercion and the seven falsy values.
I learned this I've 25 years ago.
It also helps when using JS to process potentially unreliable input. JS is also pretty nice for ETL work.
One thing I've found cool is that ~~ double bitwise not will coerce any value into a number in the 32-bit signed integer range. Not number or easily interpreted string values will be 0.
Yes it's more important to understand the loosely type languages..
Thank you sir for your valuable feedback..
5️⃣ is terribly wrong and your explanation just contradicts to itself:
Is "0" even non-empty?
Does
true == "2"
?Does
if ("0") { console.log("hi"); }
execute?Read the doc: developer.mozilla.org/en-US/docs/W...
I will update the explanation. Thanks
I've always loved these little tricks (although they do lead to some nasty mistakes sometimes lol) and this is a wonderful compilation! I also really appreciate that you made the NaN example say baNaNa
Glad you loved it and thank you for appreciating it. Happy reading..
It’s kind of frightening to see that the language that runs all of the web and a lot of backend applications has so many quirks.
Yes it should be refactored. Thanks you for the feedback..
Nice write up!
People generally love JavaScript for its duck-typing, but this article illustrates that there are quite a few traps that we can fall in to if we aren't careful.
Thank you my friend. Yes I agree with you.. we should be careful with JavaScript. I appreciate your feedback..
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍
Thank you my dear friend. I appreciate it
These results are only 'unexpected' if you don't fully understand the language and how to use it. They can also be used to your advantage.
Yes you’re right. But sometimes it happens, even for experienced developers..
Thank you for your feedback
Some comments may only be visible to logged-in visitors. Sign in to view all comments.