Why NaN === NaN returns false in JavaScript ?!

When people start exploring topics about JavaScript they get to feel a little weird sometimes and they also start to wonder why the heck do JavaScript act in such a manner, there have been even Github Repos like You Don't Know JS that explains how JavaScript works and so.

Today I wanted to explain a gotcha topic that people usually post memes about and hate on JavaScript because of such a topic ( Which I kinda understand why they do so ), I will be explaining why NaN === NaN results in a Falsy expression. Let us get started with some basics first.

What is the difference between == and ===

Whenever we compare two variables using the double equal operator we compare them using only their values, meaning if a variable A contains some value and variable B contains some other value and we did A == B what will happen is that JavaScript will check their current values and return true if they are equal.

but what if we did something like this: "1" == 1, what would be the result of such expression?

A normal person with some logical understanding would definitely guess that the output would be false because we are comparing a string to a number even if they have the same characters.

What will actually happen is that this output will be expressed as true. Why is that? When we use the double equal operator JavaScript will attempt to convert and compare operands of different types, meaning that they both would be converted to the same type and in case of comparing a number to a string JavaScript will try and convert the string to a number type like this: Number("1") == 1 which will output in that case true.

What if we have a case in which we actually want to compare the types of the variables we have and then compare their values without attempting any conversion? In that case, using the triple equal operators or the Strict equality would come in handy, what the strict equality operator simply does is checking if the operands are of the same type and then checks if they have the same value or not.

let a = "1";
let b = 1;

console.log(a == b) // true;
console.log(a === b) // false;

We could simply imagine that JavaScript under the hood does something like this when using the strict equality:

let a = "1";
let b = 1;

console.log(typeof a == typeof b && a == b)  // in case of doing a === b;

What is NaN

According to the MDN documentation NaN is:

NaN is a property of the global object. In other words, it is a variable in global scope.

So basically NaN is simply a global object that describes what a not number is or whenever we have a variable that we are attempting to convert to a number and we fail it simply gives us NaN like this:

let a = "hello world";
let convertedToNumber = Number(a);

console.log(convertedToNumber); // NaN;

What is typeof

typeof is simply a JavaScript operator that returns a string that indicates the type of an operand.

There are basically 9 types in JavaScript:

  • Undefined "undefined"
  • Null "object" (see below)
  • Boolean "boolean"
  • Number "number"
  • BigInt "bigint"
  • String "string"
  • Symbol "symbol"
  • Function object (implements [[Call]] in ECMA-262 terms) "function"
  • object "object"

Whenever typeof is used one of these types would be the result, an example for that would be:

typeof "" // string;
typeof 1 // number;
typeof function() {} // function;
typeof true // boolean;

Why NaN === NaN returns false?

And finally, let us get into the core of this article, why when we explained how strict equality works and what is NaN does this expression provide us with a falsy value?

let us simplify the answer by looking into the strict equality comparison algorithm:

let us define the following two operands: x, and y.

according to the JavaScript documentation ( This is a snippet from the docs ), the comparison algorithm works in the following matter:

The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

If Type(x) is Number, then If x is NaN, return false. If y is NaN, return false.

This means that the algorithm first checks if one of the operands is NaN before even checking their types and if so it will return false anyways.

This may be a weird implementation of the comparison algorithm but there are some workarounds for this, we could use built-in functions like Number.isNaN() to check whether the giving parameter is a NaN or not instead of comparing it with NaN directly

let invalidNumber = Number("asdasdasd"); // NaN;

let resultOfNormalComparison = invalidNumber === NaN; // false;
let resultOfBuiltInComparison = Number.isNaN(invalidNumber); // true;

Conclusion

You could somehow disagree on the implementation of the comparison algorithm but there are workarounds to check if a given variable or parameter is a NaN or not as shown above.

References: