Warum ist isNaN (null) == in JS falsch?

128

Dieser Code in JS gibt mir ein Popup mit der Aufschrift "Ich denke, Null ist eine Zahl", was ich etwas störend finde. Was vermisse ich?

if (isNaN(null)) {
  alert("null is not a number");
} else {
  alert("i think null is a number");
}

Ich verwende Firefox 3. Ist das ein Browser-Fehler?

Andere Tests:

console.log(null == NaN);   // false
console.log(isNaN("text")); // true
console.log(NaN == "text"); // false

Das Problem scheint also kein exakter Vergleich mit NaN zu sein?

Bearbeiten: Nachdem die Frage beantwortet wurde, habe ich meinen Beitrag bereinigt, um eine bessere Version für das Archiv zu erhalten. Dies macht jedoch einige Kommentare und sogar einige Antworten etwas unverständlich. Beschuldigen Sie nicht ihre Autoren. Unter den Dingen, die ich geändert habe, war:

  • Es wurde eine Notiz entfernt, die besagt, dass ich die Überschrift an erster Stelle durcheinander gebracht habe, indem ich ihre Bedeutung zurückgesetzt habe
  • Frühere Antworten zeigten, dass ich nicht klar genug darlegte, warum ich das Verhalten für seltsam hielt, und fügte daher die Beispiele hinzu, die eine Zeichenfolge überprüfen und einen manuellen Vergleich durchführen.
Hanno Fietz
quelle
3
meinst du nicht "Warum ist isNaN (null) == false"?
Matt Rogish
Basierend auf Ihrem Code gibt isnan (null) false zurück (null ist nicht "keine Zahl"), wenn "Ich denke, null ist eine Zahl" steht.
Devinmoore

Antworten:

114

Ich glaube, der Code versucht zu fragen: " xIst numerisch?" mit dem konkreten Fall hier von x = null. Die Funktion isNaN()kann verwendet werden, um diese Frage zu beantworten, bezieht sich jedoch semantisch speziell auf den Wert NaN. Aus Wikipedia für NaN:

NaN ( N ot a N umber) ist ein Wert des numerischen Datentyps, der einen undefinierten oder nicht darstellbaren Wert darstellt, insbesondere bei Gleitkommaberechnungen.

In den meisten Fällen denken wir, dass die Antwort auf "ist null numerisch?" sollte nein sein. Ist isNaN(null) == falsejedoch semantisch korrekt, da dies nullnicht der Fall ist NaN.

Hier ist die algorithmische Erklärung:

Die Funktion isNaN(x)versucht, den übergebenen Parameter in eine Zahl 1 (äquivalent zu Number(x)) umzuwandeln , und testet dann, ob der Wert gleich ist NaN. Wenn der Parameter nicht in eine Zahl konvertiert werden kann, Number(x)wird NaN2 zurückgegeben . Wenn daher die Konvertierung eines Parameters xin eine Zahl erfolgt NaN, wird true zurückgegeben. Andernfalls wird false zurückgegeben.

So in dem speziellen Fall x = null, nullwird auf die Zahl umgewandelt 0, (versuchen Sie die Bewertung Number(null)und sehen , dass es 0 zurückgibt,) und isNaN(0)gibt false zurück. Eine Zeichenfolge, die nur aus Ziffern besteht, kann in eine Zahl konvertiert werden, und isNaN gibt auch false zurück. Eine Zeichenfolge (zB 'abcd') , die nicht in eine Zahl umgewandelt werden kann , wird dazu führen , isNaN('abcd')true zurück, und zwar weil Number('abcd')Renditen NaN.

Zusätzlich zu diesen offensichtlichen Randfällen sind die numerischen Standardgründe für die Rückgabe von NaN wie 0/0.

Bei den scheinbar inkonsistenten Gleichheitstests, die in der Frage gezeigt werden, wird das Verhalten von NaNso spezifiziert, dass jeder Vergleich x == NaNfalsch ist, unabhängig vom anderen Operanden, einschließlich sich NaNselbst 1 .

Glenn Moss
quelle
8
Übrigens , NaN !== NaN. Also, ich denke, es ist nicht ganz richtig zu sagen, Number('abcd') == NaNweil Number('abcd')es NaNaber nicht gleich ist NaN. Ich liebe JavaScript.
Nilfalse
Ja. Ich meinte zu vermitteln , dass Number('abcd')ist NaNaber ich angedeutet , dass es für die Gleichstellung wahr prüft, was nicht der Fall ist. Ich werde es bearbeiten.
Glenn Moss
Ich frage mich, warum isNaNund so Numbergestaltet sind, um sich so zu verhalten.
Timidboy
1
Die Umwandlung von nullauf 0nur (zumindest in diesem Zusammenhang) tritt in der isNaN()Funktion, die ihr Argument nötigt.
Glenn Moss
3
Number (null) == 0 but parseInt (null) == NaN love JS
JoshBerke
31

Ich bin gerade selbst auf dieses Problem gestoßen.

Für mich ist der beste Weg, isNaN zu verwenden, so

isNaN(parseInt(myInt))

am Beispiel von Phyzome von oben,

var x = [undefined, NaN,     'blah', 0/0,  null, 0,     '0',   1,     1/0, -1/0,  Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
        [true,      true,    true,   true, true, false, false, false, true, true, false]

(Ich habe das Ergebnis entsprechend der Eingabe ausgerichtet und hoffe, dass es leichter zu lesen ist.)

Das scheint mir besser zu sein.

Kerl Mograbi
quelle
1
Funktioniert nicht wenn myInt= "123d". parseIntkonvertiert "123d" in 123, wodurch der isNaNTest nicht bestanden wird.
Divesh Premdeep
In der Tat, wenn Sie auch dieses Szenario erfassen möchten, schlage ich vor, meine Antwort mit der von Glenn zu kombinieren. das wird so aussehen isNaN(parseInt(str,10)) || isNaN(Number()). Übrigens - für mich ist es parseIntin Ordnung, "123d" als gültige Zahl zu betrachten , da ich laufen muss, um den numerischen Wert der Zeichenfolge zu verwenden. Ich sehe jedoch die Notwendigkeit, dieses Szenario ebenfalls zu erkennen.
Kerl Mograbi
8

(Mein anderer Kommentar verfolgt einen praktischen Ansatz. Hier ist die theoretische Seite.)

Ich habe den ECMA 262-Standard nachgeschlagen, den Javascript implementiert. Ihre Spezifikation für isNan:

Wendet ToNumber auf sein Argument an, gibt dann true zurück, wenn das Ergebnis NaN ist, und gibt andernfalls false zurück.

Abschnitt 9.3 gibt das Verhalten von an ToNumber(dies ist keine aufrufbare Funktion, sondern eine Komponente des Typkonvertierungssystems). Um die Tabelle zusammenzufassen, können bestimmte Eingabetypen ein NaN erzeugen. Dies sind Typ undefined, Typ number(aber nur der Wert NaN), jedes Objekt, dessen primitive Darstellung es ist NaN, und jedes Objekt string, das nicht analysiert werden kann. Diese Blätter undefined, NaN, new Number(NaN)und die meisten Saiten.

Jede solche Eingabe, die NaNals Ausgabe erzeugt ToNumberwird, wenn sie an übergeben wird, erzeugt eine, truewenn sie angeführt wird isNaN. Da nullerfolgreich in eine Zahl konvertiert werden kann, wird nicht produziert true.

Und deshalb.

Behandle deine Mods gut
quelle
7

Das ist in der Tat beunruhigend. Hier ist eine Reihe von Werten, die ich getestet habe:

var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]

Es wertet (in der Firebug-Konsole) Folgendes aus:

,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5

Wenn ich x.map(isNaN)anrufe (um isNaN für jeden Wert aufzurufen), erhalte ich:

true,true,true,true,false,false,false,false,false,false,false

Fazit: isNaNSieht ziemlich nutzlos aus! ( Bearbeiten : Außer es stellt sich heraus, dass isNaN nur über Number definiert ist. In diesem Fall funktioniert es einwandfrei - nur mit einem irreführenden Namen.)

Im Übrigen sind hier die Arten dieser Werte:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number
Behandle deine Mods gut
quelle
Was denkst du, was NaN bedeuten sollte? Was ist Ihrer Meinung nach so irreführend an NaN oder beim Testen darauf? Warum bist du gestört?
Bekim Bacaj
Nun, das war vor 8 Jahren, aber es sieht so aus, als wäre ich beunruhigt, dass 1) es inkonsistente Ergebnisse für Werte hat, die nicht vom Typ Number sind, und 2) es jemals wahr für etwas zurückgibt, das nicht vom Typ Number ist. Weil ein String eigentlich kein NaN ist. (Siehe auch meine andere Antwort, die erklärt, warum dies passiert.)
Behandle deine Mods gut
5

Null ist nicht NaN, und ein String ist nicht NaN. isNaN () teste einfach, ob du wirklich das NaN-Objekt hast.

Gizmo
quelle
Dann wird jedoch mindestens eine Zeichenfolge in ein NaN-Objekt umgewandelt, da isNaN ("Text") true zurückgibt.
Hanno Fietz
1

Ich bin mir nicht ganz sicher, wenn es um JS geht, aber ich habe ähnliche Dinge in anderen Sprachen gesehen, und das liegt normalerweise daran, dass die Funktion nur prüft, ob null genau gleich NaN ist (dh null === NaN wäre falsch). Mit anderen Worten, es ist nicht so, dass es denkt, dass null tatsächlich eine Zahl ist, sondern dass null nicht NaN ist. Dies liegt wahrscheinlich daran, dass beide in JS unterschiedlich dargestellt werden, so dass sie nicht genau gleich sind, genauso wie 9! == '9'.

Jason Tennier
quelle
1

In ES5 wird definiert als return isNaN (number)true, wenn das Argument zu NaN gezwungen wird, und andernfalls false.

  • Wenn ToNumber (number) NaN ist, geben Sie true zurück.
  • Andernfalls geben Sie false zurück.

Und siehe Die abstrakte Operation ToNumber convertion Tabelle . So ist es intern js Motor bewerten ToNumber(Null)ist +0, dann schließlich isNaN(null)istfalse

rab
quelle
0

Hinweis:

"1" == 1 // true
"1" === 1 // false

Der Operator == führt eine Typkonvertierung durch, === jedoch nicht.

Douglas Crockfords Website , ein Yahoo! JavaScript-Evangelist ist eine großartige Ressource für solche Dinge.

cllpse
quelle