Execute Program

Modern JavaScript: isNaN

Welcome to the isNaN lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Most programming languages support many types of numbers: at minimum, integers and floating point, but often decimal, rational, and complex numbers as well. JavaScript is different: all JavaScript numbers are floating point. Specifically, they're double-precision floating point as defined in the IEEE (pronounced "I-triple-E") 754 standard, originally published in 1985.

  • IEEE 754 floating point numbers are surprisingly complex, with many subtle details. (The 2008 revision of the IEEE 754 standard is 70 pages long!) In this lesson, we'll focus on just one floating point issue that causes a lot of problems: NaN.

  • In floating point numbers, NaN means "not a number". IEEE 754's authors intended for NaN to be used in rare cases. For example, 0/0 has no value, so they said that it should return NaN. Unfortunately, JavaScript returns NaN in many relatively common situations, often due to programming mistakes.

  • For instance, any arithmetic on undefined returns NaN.

  • >
    undefined - 1;
    Result:
  • Here's a mistake where we typo length (a real method on arrays) as lenght.

  • >
    ['a', 'b', 'c'].lenght;
    Result:
  • >
    ['a', 'b', 'c'].lenght - 1;
    Result:
    NaNPass Icon
  • Any operation on a NaN returns another NaN. Unfortunately, that means that NaNs will propagate often through your software system. When you actually see a NaN, it might be a symptom of a bug in a completely different part of the system.

  • (Note that the next code example intentionally contains the same lenght typo that we made above.)

  • >
    const lastIndex = ['a', 'b', 'c'].lenght - 1;
    const middleIndex = Math.floor(lastIndex / 2);
    middleIndex;
    Result:
    NaNPass Icon
  • Even detecting the presence of a NaN is surprisingly difficult. NaN is the only value in JavaScript that isn't equal to itself. Even when compared using ===, NaN is never equal to NaN!

  • >
    NaN == NaN;
    Result:
  • >
    NaN === NaN;
    Result:
    falsePass Icon
  • Although NaN isn't equal to itself, we can check for it with the isNaN function. This does exactly what its name suggests: it tells us whether a value is a NaN.

  • >
    isNaN(0);
    Result:
  • >
    isNaN(Infinity);
    Result:
    falsePass Icon
  • >
    isNaN(NaN);
    Result:
    truePass Icon
  • Unfortunately, isNaN also converts its argument to a number before checking. When we ask it whether isNaN(x) is true, we can imagine that we're really doing isNaN(0+x).

  • >
    0 + undefined;
    Result:
  • >
    isNaN(0 + undefined);
    Result:
    truePass Icon
  • >
    isNaN(undefined);
    Result:
    truePass Icon
  • undefined most definitely is not NaN, so this is an unambiguous mistake in JavaScript's design. But the isNaN function can't be removed or changed without breaking billions of lines of existing code, so this mistake remains in place.

  • Instead, newer versions of JavaScript have introduced a second isNaN function, Number.isNaN. Fortunately, this function behaves as we expect in every case.

  • >
    Number.isNaN(NaN);
    Result:
    truePass Icon
  • >
    Number.isNaN(0);
    Result:
    falsePass Icon
  • >
    Number.isNaN(undefined);
    Result:
    falsePass Icon
  • This may all seem confusing. That's because it is!

  • Strictly speaking, this NaN behavior is part of IEEE 754 floating point, which is used in virtually all computers today. IEEE 754's authors made these decisions for good reasons. For example: in the name of consistency, every mathematical operation should return some numerical value. That's even true of "nonsense" operations like 0/0.

  • However, any given programming language can choose how it handles edge cases like 0/0. Languages don't have to expose IEEE 754's NaN behavior to us. Many languages, including Python, throw an error when we try to do 0/0. In JavaScript, 0/0 directly gives us the dreaded NaN, leading to the problems we saw above. Opinions differ on whether this is a design mistake in JavaScript.

  • On the other hand, the existence of two isNaN functions is definitely a mistake in JavaScript's design. If you've encountered these functions in the past and wondered whether you were missing something, you weren't. The original isNaN function was a mistake, and Number.isNaN should be preferred in all circumstances.

  • It's difficult to remember that there are two isNaNs and that one of them is preferable. Often, we can use linters to ensure that we don't mix these things up, but the eslint linter doesn't have direct support for disallowing the global isNaN. Fortunately, you can configure the no-restricted-globals rule to disallow isNaN, which will have the same effect.