Some Javascript misbehaviors and why they happen

Some Javascript misbehaviors and why they happen

Introduction

Javascript is one of the most widely used languages in the software development world. Javascript developers are in high demand, especially in web development. This has caused a massive influx of entry-level developers who blitz through HTML, CSS, and just enough Javascript for some React. As you might have guessed, this impacts how well the language is comprehended generally.

Javascript is a relatively chaotic language. It can behave in unexpected ways, having you run around in circles wondering where the bug (or unsolicited feature) came from. Even developers that have worked with Javascript for years are susceptible to some of these misbehaviors. Many of these behaviors are not obvious and cannot be easily understood without being explicitly told about them. This can be a challenge for both new and experienced developers.

Below is a compilation of some of these odd occurrences and explanations for why they happen.

0.1 + 0.2 === 0.3 // false

This happens because 0.1 + 0.2 resolves into 0.30000000000000004 and not 0.3. The reason for this is because of how Javascript computes numbers. Javascript uses a 32-bit floating-point representation for numbers. What this means is that every number is represented by 32 digits of ones and zeros. The arrangement of the ones and zeros is then applied to a formula that computes the number.

From left to right, the first bit (digit) represents the sign of the number. This sign can be positive or negative. The next 8 bits represent the exponent value which specifies the size of the number. The remaining 23 bits represent the fraction value of the number.

Javascript 32-bit number system

With the 32 bits, the value is computed by this formula:

With decimal fractions, this number system causes some rounding errors in Javascript and causes some numbers not to be represented precisely. 0.1 and 0.2 are not properly represented and thus their sum ends up being off by a tiny margin.

1 == "1" //true

For developers who are familiar with data types, this can be unexpected behavior that can lead to unforeseen bugs. This error happens because the == operator doesn't bother checking the data types of both operands and only cares about their values. It forces the operand on the right to be of the same data type as the operand on the left before it compares them. In this case, it converts the string "1" to a number before running a comparison.

To avoid errors like this, always use the === operator instead. It checks for both value and type equality.

typeof([] + []) //string

Adding two empty arrays results in an empty string. Not surprising from a dynamically typed language built in 10 days. But why exactly does adding two empty arrays result in a string?

This happens because of how the + operator works. In Javascript, the operator can be used for both arithmetic addition and string concatenation (adding a string to another string). Because of its dynamic typing, Javascript always seeks to change the data type of a variable to a more convenient data type while trying to perform some action on the variable.

In this case, Javascript converts the arrays to strings using their toString() method. The toString() method for an empty array returns an empty string. Therefore, adding two empty arrays results in concatenating two empty strings, which yields another empty string.

Note that this behavior is specific to the + operator, and other arithmetic operators such as -, * and / cannot be used with arrays in Javascript.

typeof(NaN) //number

This is very weird given that NaN means "not a number". To understand why typeof(NaN) is "number", it's important to understand what NaN represents. NaN is a special value in JavaScript that is the result of an invalid or undefined mathematical operation. For example, dividing 0 by 0 or trying to calculate the square root of a negative number will result in NaN. It's important to note that NaN is not a specific number, but rather a category of values that represent different kinds of errors or undefined values.

In JavaScript, NaN is represented using the 32-bit floating-point format. If all bits of the exponent are set to 1 and at least one bit of the fraction is set to a non-zero value, it represents NaN. Thus, by definition, NaN is a number.

NaN === NaN //false

Yup! NaN is not equal to itself. This misbehavior can lead to bugs if not properly anticipated. An example of one such scenario is in the following code snippet:

function divideByTwo(number){
    let dividend = number / 2;
    if(dividend === NaN){
        console.log("You must provide a number");
    } else {
        console.log("The dividend is " + dividend);
    }
}
divideByTwo("three");

When you run the code, what you'll get in the console is "The dividend is NaN".

(dividend === NaN) returns false even though dividend is actually NaN.

The reason NaN === NaN is false is that NaN is considered an "unordered" value in JavaScript, meaning that it cannot be compared to other values using the standard comparison operators (<, >, <=, >=). Instead, JavaScript provides the isNaN() function to test whether a value is NaN or not. The isNaN() function returns true if the argument is NaN, and false otherwise.

One reason for this behavior is that NaN is not a specific value, but rather a category of values that represent different kinds of errors or undefined values. For example, there are different kinds of NaN values, such as "quiet NaN" and "signaling NaN", that have different meanings and behaviors. By treating NaN as an unordered value, JavaScript avoids making assumptions about the nature of NaN values and allows developers to handle them more flexibly.

Conclusion

Learning a language like Javascript is a never-ending process. It is impossible to avoid "unsolicited features". The key to minimizing how often these occur is by spending more time writing and reading code. Like Mark Twain said:

"Good judgment is the result of experience, and experience the result of bad judgment."