Consider we have the following mathematical operation to perform:
var a = 13 / "bar";
console.log(a); // NaN
Any mathematical operation we perform without both operands being numbers, results in the operation failing to produce a valid number which we call as NaN.
From the above operation, a is NaN and if we do:
console.log(typeof a === "number") // true
Surprised??
If NaN means not-a-number, then why typeof NaN is number?
Confusing isn't it?
Let's talk more about What exactly does NaN means?
It isn't quite accurate to think of NaN as "not a number". Rather, it is more accurate to think of it as being an "invalid number", "failed number", or even "bad number".
NaN is a kind of "sentinel value" that represents a special kind of error condition within the number set. We can think of it like: "Hey, I tried to perform a mathematic operation but failed, so here's the failed number result instead".
Now if we have some value in a variable and we want to test and see if it's this special failed number NaN, how will we do it?
One thing which will first come in our mind is to use the comparison operators and using those, we can compare the variable with the NaN itself.
Simple isn't it? Let see....
var a = 13 / "bar";
console.log(a == NaN) // false
console.log(a === NaN) // false
More confusion isn't it? Lets understand a bit more about NaN.
NaN is a very special value in that it's never equal to another NaN value.(i.e. it's never equal to itself).
So even if we do below:
console.log(a == a) // false
console.log(a === a) // false
The results in both cases is going to be false. NaN is the only value in the whole language where NaN isn't equal to itself.
Now the question arises, how do we test for it if we can't compare NaN?
For this, we use a built-in global utility function called isNaN(...).
var a = 13 / "bar";
console.log(isNaN(a)) // true
Simple enough right?
Actually, this isNaN(...) has a fatal flaw. It appears that it takes meaning of NaN as Not-a-Number too literally.
See the below example:
var a = 13 / "bar";
var b = "foo";
console.log(isNaN(a)) // true
console.log(isNaN(b)) // true
From the example above, it is just checking "if the thing passed in is either a number or not a number".
Clearly, "foo" is literally not a number, but it's definitely not the NaN value either. This is the bug in JS since very beginning.
As of ES6, finally a replacement utility has been provided: Number.isNaN(...) which solves the above problem.
Simple Polyfill for this for checking the NaN values in pre-ES6 browsers is:
if (!Number.isNaN) {
Number.isNaN = function(n) {
return(
typeof n === 'number' && isNaN(n)
);
}
}
We can implement this Polyfill in an easy way by taking advantage of the fact that NaN isn't equal to itself.
if (!Number.isNaN) {
Number.isNaN = function(n) {
return n !== n;
}
}
If you're currently using just isNaN(...) in a program, the sad reality is your program has a bug, even if you haven't been bitten by it yet. So now is the really good idea to use reliable test, like Number.isNaN(...) as provided.
That is all for the topic. Hope you like it.
Reference: You Don't know JS by Kyle Simpson
Top comments (6)
I think the function isNaN implicitly coerces the value inside it into number first. I mean :
If it can't coerce that value into a valid number, then it returns true. Example :
How about null == undefined which returns true? We know that Number(undefined) is NaN, and Number(null) is 0, but 0 == NaN returns false. So peculiar 😵💫
It is not peculiar at all if you read the specs of javascript and understand how double equals work under the hood: here
If x is
null
and y isundefined
, or vice versa, the method returns true because null and undefined are considered loosely equal.I'm on my way to learn it deeply bro, but thanks. But why could we say they are loose equal? I've tried all the possible explicit coercion between null and undefined, but the only point they're same is when we use the Boolean object wrapper form as : Boolean(null) and Boolean(undefined). Behind the loose equal, there is always coercion, and I'm learning about it right now.
Maybe because null == void 0 is true and undefined == void 0 is true too 🥴, so that's why they are loose equal 🥴.
If I can't get it on my own, I'll read this spec as you suggested me.
No worries, javascript can really be tricky sometimes, it's good that you're trying to learn more about it :)
When we talk about "loose" equality in JavaScript, we're referring to the == operator, which allows for type coercion. This is different from "strict" equality (===), which does not.
The JavaScript spec indeed states that null and undefined are considered loosely equal, which is why null == undefined returns true. This is a special rule, separate from the normal rules of type coercion.
When you do null == 0, it returns false because null and 0 aren't the same type, and there's no rule that allows null to be coerced into 0. However, null and undefined have that special exception in the specification, hence they are loosely equal.
void 0 is another way to produce undefined, so null == void 0 is also true due to the special rule.
So the behavior isn't really about what values null and undefined coerce to in various contexts, but rather about the specific rules for == in the JavaScript spec.
I hope that it was clear :D
awesome