0

I just don't get it:

isNaN([]) === false -> true

In the browser or in node.js

I'd like to understand why.

Chin Leung
  • 14,621
  • 3
  • 34
  • 58
SkinyMonkey
  • 259
  • 1
  • 3
  • 7
  • That's the answer I would expect; it seems intuitive. Please explain what you think that result means and why you think it is confusing. – Sam Hartman Nov 08 '17 at 20:42
  • @SamHartman, "`[]` is a number", is intuitive?? You have a strange definition of the word – smac89 Nov 08 '17 at 20:45
  • Because `isNaN([])` returns `false` and `false === false` is `true`. – Chin Leung Nov 08 '17 at 20:45
  • `[]` is converted to `0`, and 0 is a number. `Number([]);` – Chin Leung Nov 08 '17 at 20:46
  • it's coerced, not converted. there's a slight difference. In practice, conversion (`parseInt([])`) returns NaN – Nicolas Straub Nov 08 '17 at 20:49
  • 1
    @smac89 `isNaN` tests whether *a number* is the special `NaN` value. Calling it on non-numbers is a misuse in the first place. But yes `[]` as a number is not `NaN`. – Bergi Nov 08 '17 at 21:06
  • 1
    Closely related: [Why does `isNaN(" ")` equal false?](https://stackoverflow.com/q/825402/1048572) – Bergi Nov 08 '17 at 21:09

2 Answers2

1

this has to do with type coercion. when isNaN receives anything but a number, it coerces the value to a number... so it basically does +value. since +[] is 0 and 0 is a number, isNaN returns false.

If you want to check whether a value is not a number, you need to convert it with parseInt (or parseFloat). both those functions will return NaN for an empty array.

The difference is with conversion you're explicitly telling it the input is number-like and should therefore be able to be converted into a value of type Number without issues. Since [] is not number-like, trying to parse (convert) it will result in NaN.

This is an extremely simplified explanation of how parsing and conversion work. For one, they are two separate things. if you want to learn more about how these two work, start here

With coercion, however (which is what you're doing), you essentially passed it a value and "assumed" (for lack of a better word) the values type is compatible with the parameters' requirements. So isNaN receives an array and guesses you meant to pass it a number, and coerces it to its number equivalent (in the case of an empty array, 0) before performing the actual operation. so isNaN is performed on 0, not an empty array.

Again, this explanation is oversimplified. for more information about type coercion in JS, go here

Nicolas Straub
  • 3,381
  • 6
  • 21
  • 42
0

NAN is what is returned when a math function fails. NaN is present in almost all languages that do floating point math. However some Javascript functions (and the internal logic that coerces values) also return NaN when values are coerced to being a number that do not have a numeric representation. It's a lot easier to understand why isNaN is defined the way it is if you think about NaN as being intended for the case where your math gave a nonsense answer and ignore its role in representing numeric coersion failures. Obviously when writing Javascript, you'll need to consider (and use) the coersion NaN, but this particular language design choice makes more sense if you ignore that for the moment.

So, for example math.sqrt(-1) is NAN. the isNAN function returns true if and only if its input argument is NAN. Since [] is not NAN, I'd expect isNAN([]) to return false. isNAN is the wrong function for determining if something is a number. It does have strange coersion rules, buteven without them, the answer would be the same in the case of isNAN([]. [] is not theNAN object, so isNAN([]) is false. As @Nicolás Straub points out, the implementation happens to coerce [] to a number first, but we'd expect the same result given the interface contract of isNAN even if it did not.

ReferenceError: ISNAN is not defined
at repl:1:1
at REPLServer.defaultEval (repl.js:272:27)
at bound (domain.js:287:14)
at REPLServer.runBound [as eval] (domain.js:300:12)
at REPLServer.<anonymous> (repl.js:441:12)
at emitOne (events.js:82:20)
at REPLServer.emit (events.js:169:7)
at REPLServer.Interface._onLine (readline.js:203:10)
at REPLServer.Interface._line (readline.js:542:8)
at REPLServer.Interface._ttyWrite (readline.js:819:14)

isNaN [Function: isNaN]

isNaN("bar") true

However, "bar" is not actually the IEEE not a number floating point construct, so we'd expect that a function would tell us "bar" is not NaN. Because isNaN coerces its argument to a number, it gives us the wrong answer. The es6 Number.isNaN function would give us the correct answer "bar" is not NaN.

Sam Hartman
  • 6,210
  • 3
  • 23
  • 40
  • Remember NaN is a Number (strange as it may seem), so the behaviour has everything to do with coercion – Nicolas Straub Nov 08 '17 at 21:02
  • @Nicolás Straub they have everything to do with the implementation and nothing to do with the interface contract. You want isNAN to return false for every object in the universe except for NAN. – Sam Hartman Nov 08 '17 at 22:44
  • OK, without getting into too much detail, let's first look at your example. Math.sqrt doesn't try to get the square root of a negative number, it does `if val < 0 return NaN`, so, internally, no mathematical operation was performed. If you do the same, but with `"1"`, the output is `1`, because the value put in is implicitly converted (coerced) to a number. In the case of the OP question `isNaN([])` returns false because, again, the array is coerced to a number. The process is as so: [].valueOf() is called, which returns an empty string, which is a native type. that, in turn, is converted – Nicolas Straub Nov 08 '17 at 22:51
  • to a number explicitly (not exactly casting, but let's call it that), so the operation can be performed, because isNaN requires a number to operate. since casting an empty string to a number results in 0 in JS, isNaN returns false because it performs the operation against the number 0. if you use parseInt, it returns NaN because the first character of the array, converted via the toString method, is not a number, not because you didn't actually pass a number in. in fact, `parseInt([1,2,3])` results in 123, whereas `isNaN([1,2,3])` equals `true`. I won't continue to comment because of space – Nicolas Straub Nov 08 '17 at 22:58
  • and BTW, I didn't downvote your answer because I don't agree with you, I did it because I find it incomplete to the point of being misleading. – Nicolas Straub Nov 08 '17 at 23:03
  • @Nicolás Straub I agree that my answer was incomplete to the point of being misleading. I've expanded it significantly; thanks for the motivation to do so. – Sam Hartman Nov 09 '17 at 02:23
  • I removed the downvote. just want to say coercion is critical though. For example, `isNaN(parseInt([1,2,3]))` returns `false` but `isNaN([1,2,3])` returns `true`. This (and a ton of other gremlins in JS's type conversion approach) is why it's very important to have at least a basic understanding of how coercion works. Otherwise, you end up confused in a pit of code behaving extremely poorly, scratching your head and asking yourself 'Why??' (as the OP was). I've been there more times than I care to admit with this language, which is why I feel understanding coercion is so important – Nicolas Straub Nov 09 '17 at 15:29