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
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'
NaN Has a falsy value
Boolean(NaN); // false
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
You can access NaN in four different ways.
NaN;
this.NaN;
globalThis.NaN;
Number.NaN
Avoid comparisons with NaN
NaN > 0; // false
NaN >= 0; // false
NaN < 0; // false
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 ❌
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
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! ✅
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;
};
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 ❌
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 ✅
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);
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;
}
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();
Resources and citations
-
NaN -
isNaN -
Number.isNaN - Related topics
- freeCodeCamp: JavaScript type coercion explained https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/
- SitePoint ES6 New Number Methods https://www.sitepoint.com/es6-number-methods/
Top comments (1)
This was really helpful to me. thanks 👍