ariya.io About Talks Articles

The Curious Case of JavaScript NaN

3 min read

NaN, not a number, is a special type value used to denote an unrepresentable value. With JavaScript, NaN can cause some confusion, starting from its typeof and all to the way the comparison is handled.

Several operations can lead to NaN as the result. Here are some examples (follow along on JSBin: jsbin.com/yulef):

Math.sqrt(-2)
Math.log(-1)
0/0
parseFloat('foo')

The first trap for many JavaScript beginners is usually the unexpected result of calling typeof:

console.log(typeof NaN);   // 'number'

In a way, while NaN isn’t supposed to be a number, its type is number. Got it?

Stay calm, as this will continue to lead to many confusing paths. Let’s compare two NaNs:

var x = Math.sqrt(-2);
var y = Math.log(-1);
console.log(x == y);      // false

Maybe that’s because we’re supposed to use strict equal (===) operator instead? Apparently not.

var x = Math.sqrt(-2);
var y = Math.log(-1);
console.log(x === y);      // false

Arrgh! Could it be because they are NaNs from two different operations? What about…

var x = Math.sqrt(-2);
var y = Math.sqrt(-2);
console.log(x == y);      // false

Even crazier:

var x = Math.sqrt(-2);
console.log(x == x);      // false

What about comparing two real NaNs?

console.log(NaN === NaN); // false

Because there are many ways to represent a NaN, it makes sense that one NaN will not be equal to another NaN. Still, this is the reason why I sometimes tweet:

To solve this, originally I intended to submit this proposal for ECMAScript 7:

nan

But of course, solutions (and workarounds) already exist today.

Let’s get to know the global function isNaN:

console.log(isNaN(NaN));      // true

Alas, isNan() has its own well-known flaws:

console.log(isNaN('hello'));  // true
console.log(isNaN(['x']));    // true
console.log(isNaN({}));       // true

This often leads to a number of different workarounds. One example is to exploit the non-reflective nature of NaN (see e.g. Kit Cambridge’s note):

var My = {
  isNaN: function (x) { return x !== x; }
}

Another example is to check for the value’s type first (to prevent coercion):

My.isNaN = function(x) { return typeof x === 'number' && isNaN(x); };

Note: The coercion that is being blocked here is related to isNaN. As an exercise, compare the result of isNaN(2), isNaN('2') and isNaN('two').

Fortunately, for the upcoming ECMAScript 6, there is Number.isNaN() which provides a true NaN detection (BTW, you can already use this function in the latest version of Chrome and Firefox). In the latest draft from April 2014 (Rev 24), this is specified in Section 20.1.2.4:

When the Number.isNaN is called with one argument number, the following steps are taken:

  1. If Type(number) is not Number, return false.

  2. If number is NaN, return true.

  3. Otherwise, return false.

In other words, it returns true only if the argument is really NaN:

console.log(Number.isNaN(NaN));            // true
console.log(Number.isNaN(Math.sqrt(-2)));  // true
 
console.log(Number.isNaN('hello'));        // false
console.log(Number.isNaN(['x']));          // false
console.log(Number.isNaN({}));             // false

Next time you need to deal with NaN, be extremely careful!

Related posts:

♡ this article? Explore more articles and follow me Twitter.

Share this on Twitter Facebook