DEV Community

Cover image for Unexpected Moments of JavaScript That Will Challenge Your Understanding of the Language
Code of Relevancy
Code of Relevancy

Posted on • Updated on

Unexpected Moments of JavaScript That Will Challenge Your Understanding of the Language

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
Enter fullscreen mode Exit fullscreen mode

== 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
Enter fullscreen mode Exit fullscreen mode

2️⃣

[] == ![] // true
Enter fullscreen mode Exit fullscreen mode

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" 
Enter fullscreen mode Exit fullscreen mode

3️⃣

null == undefined // true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

4️⃣

typeof NaN   // number
typeof null  // object
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

5️⃣

true == "1"  // true
false == "0" // true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

6️⃣

"1" + 1    // "11"
2 + "2"    // "22"
"5" - 3    // 2
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

7️⃣

"b" + "a" + + "a" + "a" // "baNaNa"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

9️⃣

7 > 6 > 5 // false
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

1️⃣0️⃣

Math.max() // -Infinity
Math.min() // Infinity
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

1️⃣2️⃣

(function(x) { delete x; return x; })(1);   // 1
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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:

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!

YouTube
Discord
GitHub

Dour Darcel #8740

Top comments (47)

Collapse
 
tohodo profile image
Tommy • Edited

Good stuff. Another common gotcha:

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Collapse
 
wintercounter profile image
Victor Vincent

If someone wants to overcome this while using var, not many people know that setTimeout can accept params to be passed to the callback, great alternative to bind.

for (var i = 0; i < 3; ++i) {
  setTimeout(a => console.log(a), 1000, i);
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tohodo profile image
Tommy

Good tip. I used this pattern a lot before ES6.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Good fix

Collapse
 
codeofrelevancy profile image
Code of Relevancy

I gave your suggestion a space, here it's..

Image description

Thank you again.. Happy reading!!!

Collapse
 
scofieldidehen profile image
Scofield Idehen

this is insightful.

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy

Yes

Collapse
 
codeofrelevancy profile image
Code of Relevancy

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

Collapse
 
joydeep-bhowmik profile image
Joydeep Bhowmik

This was really informative and solved one of most annoying glitch of my small project. Thanks

Collapse
 
codeofrelevancy profile image
Code of Relevancy

You're welcome my friend. Glad you found it helpful and thank you for your feedback..

Collapse
 
aarone4 profile image
Aaron Reese

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

I appreciate your input on this article

Collapse
 
tracker1 profile image
Michael J. Ryan

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.

Collapse
 
tracker1 profile image
Michael J. Ryan

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Yes it's more important to understand the loosely type languages..

Thank you sir for your valuable feedback..

Collapse
 
zetaraku profile image
Raku Zeta • Edited

5️⃣ is terribly wrong and your explanation just contradicts to itself:

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.

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...

Collapse
 
codeofrelevancy profile image
Code of Relevancy • Edited

Image description

I will update the explanation. Thanks

Collapse
 
mergerg profile image
Raphael

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

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Glad you loved it and thank you for appreciating it. Happy reading..

Collapse
 
zirkelc profile image
Chris Cook

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Yes it should be refactored. Thanks you for the feedback..

Collapse
 
ant_f_dev profile image
Anthony Fung

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you my friend. Yes I agree with you.. we should be careful with JavaScript. I appreciate your feedback..

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you my dear friend. I appreciate it

Collapse
 
jonrandy profile image
Jon Randy 🎖️

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

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.