5 JavaScript assumptions that stole my time

Through my career, I have come to the realization that “assumptions” is in itself an area with a lot of depth. As software developers, we constantly make assumptions about our own and other people’s code not to mention the languages and tools we work with.

When debugging we are required to challenge these assumptions, but in a balanced fashion, where one needs to classify assumptions as to not spend unnecessary time questioning stuff we should simply trust will behave as expected.

When you have a problem with your code the most productive and humble approach is to feel responsible for it and try to fix what you yourself have written.

When I started writing JavaScript on a more serious level I had this approach to software development. But by approaching it this way, there were a few things that cost me quite a lot of time debugging. I ran into each of them in more complex scenarios but simply testing each of these with reduced test cases (shown below), would have saved me a lot of time. My assumptions, however, were rock solidly rooted in my subconscious.

Let’s have a look

1. Floating-Point Arithmetic

0.2 + 0.4 === 0.6 // true?
console.log(0.2 + 0.4) // 0.6?

Yup seems pretty simple, but the truth is that the first line will evaluate to false. Why? Because JavaScript uses floating point arithmetic based on the IEEE standard. This has consequences for how JavaScript handles its numbers since not every number can be properly represented. So what do we do instead?

0.2 + 0.4 === 0.6 // false!
console.log(0.2 + 0.4) // 0.6000000000000001!

Since the above is what you actually get we need to approach this in a different way and sadly this is not a simple one-off. Instead, I will point you to these 2 threads on stack overflow.

1. Is JavaScript’s Math broken?
2. Elegant workaround for JavaScript floating point number problem

For an in depth explanation please refer to
3. What Every Computer Scientist Should Know About Floating-Point Arithmetic.

2. parseInt radix

parseInt('09') === 9 // true?

Once again a very simple one-liner. The problem here is that the radix (base) JavaScript will use depends on the input it gets. In this case, because the input starts with zero parseInt will use radix 8.

To solve this problem, you should instead always specify what radix to use.

parseInt('09') === 9 // false!
parseInt('9') === 9 // true!

parseInt('09', 10) === 9 // true!
parseInt('9', 10) === 9 // true!

By explicitly stating what radix parseInt should be using we eliminate reader confusion and guarantee predictable behavior.

To read more about how the radix is selected please refer to developer.mozilla.org

3. Block scope variables

var age = 28;

var someCondition = true;
if (someCondition) {
	var age = 10;
	console.log(age); // 10?
}

console.log(age); // 28?

I wrongly assumed that the second ‘age’ variable was local to the conditional if block. But the truth is the JavaScript does not have block scope but only function scope.

var age = 28;

var someCondition = true;
if (someCondition) {
	var age = 10;
	console.log(age); // 10!
}

console.log(age); // 10!

What happens is that the outer age variable is overwritten and assigned the value of 10.

4. NaN is not a Number

typeof NaN === 'number' // false?
NaN === NaN // true?

This was what I would have expected. I was wrong though.

typeof NaN === 'number' // true!
NaN === NaN // false!

What in reality happens is the above. I really did not think the type of NaN was ‘number’ since it seem counter intuitive to the name ‘Not A Number’. On top of that I was assuming that whatever the hell NaN was, it was definitely NaN! Wrong.

As you can see NaN does not equal NaN since NaN is not considered equal to another NaN because they may be different values. Say what? Trust me this will also make sense once you understand the behavior of NaN properly. This however is outside the scope of this article but you can expand your understanding of the subject here.

If all you want to do right now is find a way of testing for NaN you should be doing this:

isNaN(Math.sqrt(-4)) // true!
isNaN(NaN) // true!

5. Array ‘for in’ loop

var nums = [5, 6, 7, 8, 9];
for (var i in nums) {
	console.log(i); // 5 6 7 8 9?
}

I come from the world of C# thinking that this was JavaScript syntax for what was effectively a for-each loop. What instead happens is this.

var nums = [5, 6, 7, 8, 9];
for (var i in nums) {
	console.log(i); // 0 1 2 3 4!
}

As you can see, what you actually get is the indexes of said array instead of the values. In my example here it is very clear but if the values had instead been 1,2,3… the result would have seemed just off by 1.

Now the ‘for-in’ statement is not bad practice at all but it has completely different behavior than I assumed. The purpose of ‘for-in’ is to enumerate over object properties not values in an array.

When you do want to iterate through the values of an array you should be doing this:

var nums = [5, 6, 7, 8, 9];
for (var i = 0; i < nums.length; i++) {
	console.log(nums[i]); // 5 6 7 8 9!
}

Note: code below is also not advised:

//Array.prototype.foo = 123; <-- this line will make the output 5 6 7 8 9 123

var nums = [5, 6, 7, 8, 9];
for (var i in nums) {
	console.log(nums[i]); // 5 6 7 8 9!
}

Summary

Once you understand the behavior above you are able to write sound code the is readable and predictable. If however you are not aware of them JavaScript can seem buggy, broken and a pain. It is not!


Warning: count(): Parameter must be an array or an object that implements Countable in /var/www/whoknew.dk/public_html/wp-includes/class-wp-comment-query.php on line 399

Speak Your Mind

*