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 👍