DEV Community

Cover image for Everything you need to know about NaN in JavaScript
Sam Sedighian
Sam Sedighian

Posted on • Originally published at Medium

Everything you need to know about NaN in JavaScript

NaN is a global property that represents the value of Not-A-Number, hence the name. It is possible to get the value NaN returned when doing an arithmetic operation or coercing some value to a number. Here are some operations that result in NaN

0 / 0; // NaN
Infinity / Infinity; // NaN
1 / undefined; // NaN
undefined / 1; // NaN

// [almost] all arithmetic operation involving NaN

NaN + 1; // NaN
NaN - 1; // NaN
NaN * 1; // NaN
NaN / 1; // NaN
NaN ** 1; // NaN
NaN % 1; // NaN
// with the exception of
NaN ** 0; // 1

// Finally, coercing any value that does not have a numeric value

parseInt('hi', 10); // NaN
parseFloat(undefined); // NaN
+"hi"; // NaN
Number({}); // NaN
"hi" - 1; // NaN
"hi" * 1; // NaN
"hi" / 1; // NaN
"hi" % 1; // NaN
"hi" ** 1; // NaN
Enter fullscreen mode Exit fullscreen mode

it is worth mentioning that most of the confusion about NaN comes from the behavior of coercing a non-numeric-value to a numeric-value which results in NaN. For this reason, I recommend getting yourself familiarized with the last few examples from the code above and better yet why some values such as booleans, [1] and '' do not result in NaN

Interesting facts about NaN

NaN has a bad reputation for being tricky, however, if you familiarize yourself with the following few facts you will be able to work with NaN with no issue.

NaN unlike it's name is actually from the type Number

typeof NaN; // 'number'
Enter fullscreen mode Exit fullscreen mode

NaN Has a falsy value

Boolean(NaN); // false
Enter fullscreen mode Exit fullscreen mode

NaN is the only value in JavaScript that does not equal itself. Hint: this will become useful later on.

NaN === NaN; // false
NaN == NaN; // false
NaN !== NaN; // true

// No, it is not pointing to a differnt NaN object (no such thing)
const iAmNaN = NaN;
iAmNaN == iAmNaN; //false
Enter fullscreen mode Exit fullscreen mode

You can access NaN in four different ways.

NaN;
this.NaN;
globalThis.NaN;
Number.NaN
Enter fullscreen mode Exit fullscreen mode

Avoid comparisons with NaN

NaN > 0; // false
NaN >= 0; // false
NaN < 0; // false
Enter fullscreen mode Exit fullscreen mode

Let's look at an example

Let's say we have a function that takes one argument and increments it by 10 . We want to accept both numbers and strings representing a number so we will use parseFloat

const incrementByTen = function(val) {
  const n = parseFloat(val, 10);
  return n + 10;
};

incrementByTen(0); // 10 ✅
incrementByTen('2.3'); // 12.3 ✅

/*
  result of parseFloat will be NaN in examples below
  hence the addition operations will also return NaN
*/
incrementByTen(NaN); // NaN ❌
incrementByTen(false); // NaN ❌
incrementByTen({}); // NaN ❌
incrementByTen([]); // NaN ❌
incrementByTen('a'); // NaN ❌
incrementByTen(true); // NaN ❌
incrementByTen(['a', 1]); // NaN ❌
Enter fullscreen mode Exit fullscreen mode

We just learned there are plenty of arguments which would result in NaN. Perhaps a better way to handle this is to throw an error for those cases. However, as we learned earlier the usual comparisons will not work for NaN as we can see below. For this reason, we will use the global function isNaN.

typeof NaN === NaN; // false
NaN === NaN; // false
Enter fullscreen mode Exit fullscreen mode

what is isNaN and how it works?

isNaN is a global function, takes a single argument and returns a boolean indicating whether or not the argument passed is NaN. MDN explains isNaN as such:

The function [ isNaN ] should be interpreted as answering the question, "is this value, when coerced to a numeric value, an IEEE-754 'Not A Number' value?"

We now write our function with isNaN to throw an error when the result of the parseFloat is NaN.

const incrementByTen = function(val) {
  const n = parseFloat(val, 10);
  if (isNaN(n)) {
    throw new Error('Resulted in NaN!');
  }
  return n + 10;
};

incrementByTen(0); // 10 ✅
incrementByTen('2.3'); // 12.3 ✅
incrementByTen(NaN); // Error: Resulted in NaN! ✅
incrementByTen(false); // Error: Resulted in NaN! ✅
incrementByTen({}); // Error: Resulted in NaN! ✅
incrementByTen([]); // Error: Resulted in NaN! ✅
incrementByTen('a'); // Error: Resulted in NaN! ✅
incrementByTen(true); // Error: Resulted in NaN! ✅
incrementByTen(['a', 1]); // Error: Resulted in NaN! ✅
Enter fullscreen mode Exit fullscreen mode

Great, our function works as expected. Now let's learn a bit more about isNaN. Best way to understand how isNaN works is to create our own [basic version] polyfill for it. Polyfill is not required to use isNaN, It is super old...IE 3 old! 👴🏽

const isNaN = function(value) {
  // coercing it into a numeric value. BEWARE OF THIS LINE
  const n = Number(value);
  // now checking to see if it does not equal itself
  // only NaN does not equal itself 🤯
  return n !== n;
};
Enter fullscreen mode Exit fullscreen mode

When working with isNaN you need to beware of the coercion of the value to a numeric-value. Remember some values cannot be coerced to a numeric-value and will result in NaN so even though your argument to isNaN might not have been NaN it could become one.

Here are a few examples where this happens and isNaN does not work as we perhaps expect it to:

isNaN(NaN); // true ✅

isNaN(undefined); // true ❌
isNaN('a'); // true ❌
isNaN({}); // true ❌
isNaN(['a']); // true ❌
isNaN(10n); // TypeError: Cannot convert a BigInt value to a number ❌
isNaN(Symbol()); // Uncaught TypeError: Cannot convert a Symbol value to a number ❌
Enter fullscreen mode Exit fullscreen mode

Number.isNaN to the rescue 🦸🏻‍♀️

For the reasons that should be clear from above using isNaN is not ideal. This is why Number.isNaN has been added to JavaScript starting from ES6. The main difference between the two functions is that Number.isNaN does not convert its argument to a numeric-value before determining whether it is NaN.

Number.isNaN(NaN); // true ✅

Number.isNaN(undefined); // false ✅
Number.isNaN('a'); // false ✅
Number.isNaN({}); // false ✅
Number.isNaN(['a']); // false ✅
Number.isNaN(10n); // false ✅
Number.isNaN(Symbol()); // false ✅
Enter fullscreen mode Exit fullscreen mode

Great, it is working as expected. I recommend to always use Number.isNaN. Even if you want to coerce the value to a numeric-value do it yourself and then use Number.isNaN that way you are clearly expressing your intentions.

// Bad
isNaN(someValue);

// Good
Number.isNaN(someValue)

// And if we do need someValue to be coerced to a numeric-value
const numericalValue = +someValue; // or Number(someValue)
Number.isNaN(numericalValue);
Enter fullscreen mode Exit fullscreen mode

Alternatives to native isNaN and Number.isNaN

As you can imagine before Number.isNaN was introduced there were some workarounds for us to handle this which perhaps are no longer needed but worth noting.

Write your own

If you are not going to use Number.isNaN, this is perhaps the quickest and fastest way to get going. The key to understanding this function is that isNaN is the only value that does not equal itself.

const _isNaN = function(value) {
  return value !== value;
}
Enter fullscreen mode Exit fullscreen mode

Lodash and Underscore

Both of these popular utility libraries have their own version of the functionality which works similar to Number.isNaN

import lodash from 'lodash';
import underscore from 'underscore';

lodash.isNaN();
underscore.isNaN();
Enter fullscreen mode Exit fullscreen mode

Resources and citations

Top comments (1)

Collapse
 
ghawri_tarun profile image
Tarun Ghawri

This was really helpful to me. thanks 👍