Was ist der Unterschied zwischen (NaN! = NaN) und (NaN! == NaN)?

148

Zunächst möchte ich erwähnen, dass ich weiß wie isNaN()und Number.isNaN()arbeite. Ich lese The Definite Guide von David Flanagan und er gibt ein Beispiel dafür, wie man überprüft, ob der Wert ist NaN:

x !== x

Dies führt truegenau dann zu, wenn dies der Fall xist NaN.

Aber jetzt habe ich eine Frage: Warum verwendet er einen strengen Vergleich? Weil es so scheint

x != x

verhält sich genauso. Ist es sicher, beide Versionen zu verwenden, oder fehlen mir einige Werte in JavaScript, die truefür x !== xund falsefür zurückgegeben werden x != x?

Giorgi Nakeuri
quelle
10
Es könnte sein, dass Flanagan nur !==Schecks gegenüber !=Schecks bevorzugt . Soweit mir bekannt ist, gibt es keinen anderen Wert, wo x != x. Es gibt jedoch zwei unterschiedliche Gruppen von JavaScript-Entwicklern: diejenigen, die es vorziehen, !=und diejenigen, die es bevorzugen !==, sei es aus Gründen der Geschwindigkeit, Klarheit, Ausdruckskraft usw.
Steve Klösters
30
Warum einen losen Vergleich verwenden, wenn sich ein strikter Vergleich genauso verhält?
Ry-
3
@ Raulucco: NaNist kein eindeutiger Typ, es ist eine Zahl. Es ist ein einzigartiger Wert , der sich selbst nicht entspricht.
TJ Crowder
8
Der Titel scheint irreführend zu sein. Ich würde vorschlagen, es in etwas wie "Unterscheidet sich x! = X jemals von x! == x?" Zu ändern.
TJ Crowder
6
@femmestem: Giorgi sagte "in diesem Fall", es ist eine Frage des Stils. Und da hat er recht. Es ist kein Stil, wenn die Typen der Operanden unterschiedlich sind, aber es ist Stil, wenn sie gleich sind. Separat: Flanagan führt diese Vergleiche ===mit NaN durch , um darauf hinzuweisen, dass NaN nicht gleich sich selbst ist. Er ist nicht "falsch", er macht es als Unterrichtsübung und zeigt, dass es nicht funktioniert.
TJ Crowder

Antworten:

128

Lassen Sie mich zunächst darauf hinweisen, dass dies NaNein ganz besonderer Wert ist: Per Definition ist er nicht gleich sich selbst. Das kommt vom IEEE-754-Standard, auf den sich JavaScript-Zahlen stützen. Der Wert "keine Zahl" ist niemals gleich sich selbst, selbst wenn die Bits genau übereinstimmen. (Was in IEEE-754 nicht unbedingt der Fall ist, ermöglicht es mehrere unterschiedliche "keine Zahl" -Werte.) Aus diesem Grund wird dies sogar angezeigt. Alle anderen Werte in JavaScript sind gleich, NaNist nur etwas Besonderes.

... fehlt mir ein Wert in JavaScript, der für x! == x true und für x! = x false zurückgibt?

Nein, du bist nicht. Der einzige Unterschied zwischen !==und !=besteht darin, dass letztere bei Bedarf Typzwang ausüben, um die Typen der Operanden gleich zu machen. In x != xsind die Typen der Operanden die gleichen und daher genau die gleichen wie x !== x.

Dies ist vom Beginn der Definition der abstrakten Gleichheitsoperation an klar :

  1. ReturnIfAbrupt (x).
  2. ReturnIfAbrupt (y).
  3. Wenn Typ (x) mit Typ (y) identisch ist, dann

    Geben Sie das Ergebnis der Durchführung eines strengen Gleichheitsvergleichs x === y zurück.

  4. ...

Die ersten beiden Schritte sind grundlegende Sanitärinstallationen. In der Tat besteht der allererste Schritt ==darin, zu prüfen, ob die Typen gleich sind, und wenn ja, dies ===stattdessen zu tun . !=und !==sind nur negierte Versionen davon.

Wenn Flanagan also richtig ist, dass nur NaNwahr geben wird x !== x, können wir sicher sein, dass es auch wahr ist, dass nur NaNwahr geben wird x != x.

Viele JavaScript-Programmierer verwenden ===und !==vermeiden standardmäßig einige Fallstricke im Zusammenhang mit dem Typenzwang, den die losen Operatoren ausüben, aber in diesem Fall gibt es nichts zu lesen, was Flanagan mit dem strengen oder losen Operator zu tun hat.

TJ Crowder
quelle
Ich habe den 4.9.1 - Equality and Inequality OperatorsAbschnitt noch einmal gelesen und dies scheint die Antwort zu sein. Der entscheidende ===Vergleichspunkt ist : If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Giorgi Nakeuri
@GiorgiNakeuri: Ich bin nicht sicher, auf was 4.9.1 Sie sich beziehen, vielleicht Flanagans Buch? Aber das sagt im Grunde, was das Zitat aus der obigen Spezifikation sagt, ja.
TJ Crowder
2
Ich akzeptiere dies, weil dies meine Frage formalisiert und präzise beantwortet. Danke für die Erklärungen!
Giorgi Nakeuri
1
@ Moshe: Was meinst du mit "Live-Bindungen"? (Der Begriff erscheint nicht in der Spezifikation.) Meinen Sie so etwas wie das Beispiel von GOTO 0, in dem aes sich tatsächlich um eine Funktion handelt und der nicht zweimal denselben Wert zurückgibt? Das ist nicht dasselbe wie ein Wert, für den !==es wahr wäre, worum das OP gebeten hat. Es ist nur eine Funktion, die unterschiedliche Werte zurückgibt. foo() !== foo()ist auch nicht unbedingt wahr, da foobei jedem Aufruf möglicherweise andere Werte zurückgegeben werden.
TJ Crowder
1
@ Moshe Nun, das ist eine super böse Art, mit Eigenschaften und Gettern herumzuspielen. Aber es scheint ziemlich genau das gleiche zu sein wie das Beispiel von GOTO 0, nur mit einer zusätzlichen Indirektionsebene.
JAB
37

Für NaN-Zwecke !=und !==machen Sie dasselbe.

Viele Programmierer vermeiden jedoch ==oder !=in JavaScript. Zum Beispiel betrachtet Douglas Crockford sie als die " schlechten Teile " der JavaScript-Sprache, weil sie sich auf unerwartete und verwirrende Weise verhalten:

JavaScript hat zwei Sätze von Gleichheitsoperatoren: ===und !==, und ihre bösen Zwillinge ==und !=. Die Guten arbeiten so, wie Sie es erwarten würden.

... Mein Rat ist, niemals die bösen Zwillinge zu benutzen. Verwenden Sie stattdessen immer ===und !==.

jkdev
quelle
2
Die Frage bezieht sich nicht auf NaN (trotz des Titels). Die Frage lautet: "Fehlt mir ein Wert in JavaScript, der für x! == x true und für x! = X false zurückgibt?"
TJ Crowder
@TJCrowder Zwei Fragen, wirklich. Die erste Frage lautet "Ist es sicher, beide Versionen zu verwenden?" Und die Antwort lautet, dass beide Versionen gleichwertig sind. Ich mag Ihre Antwort "unter der Haube", die alles im Detail erklärt.
JKDEV
22

Lassen Sie mich Ihnen zum Spaß ein künstliches Beispiel zeigen, bei dem xsich NaNdie Bediener ohnehin nicht anders verhalten. Definieren Sie zuerst:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

Dann haben wir

x != x // false

aber

x !== x // true
GOTO 0
quelle
9
Ha! :-) Aber foo() != foo()hier gibt foo effektiv 1 und dann 2 zurück. ZB sind die Werte nicht gleich, es werden nur verschiedene Werte verglichen.
TJ Crowder
2

Ich möchte nur darauf hinweisen, dass dies NaNnicht das einzige ist, was x !== xohne Verwendung des globalen Objekts produziert wird. Es gibt viele clevere Möglichkeiten, dieses Verhalten auszulösen. Hier ist einer, der Getter verwendet:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

Wie andere Antworten zeigen, ==führt die Typumwandlung durch, aber in wie in anderen Sprachen und gemäß dem Standard - NaN zeigt einen Rechenfehler an und ist aus guten Gründen nicht gleich sich selbst.

Aus irgendeinem Grund, den ich nicht kenne, ist dies ein Problem mit JS, aber die meisten Sprachen mit Doppelbildern (nämlich C, Java, C ++, C #, Python und andere) weisen genau dieses Verhalten auf, und die Leute sind damit einverstanden.

Benjamin Gruenbaum
quelle
2
Ja, genau das hat @TJCrowder im Kommentar zur Antwort von GOTO_0 erwähnt, nicht wahr?
Giorgi Nakeuri
Könnten Sie klarstellen, wie Sie den mehrdeutigen Zwang in diesen anderen Sprachen erhalten können?
Chicocvenancio
0

Da Bilder manchmal besser sind als Wörter, überprüfen Sie diese Tabelle (was für mich der Grund ist, dies zu einer Antwort zu machen, stattdessen ist ein Kommentar, weil es eine bessere Sichtbarkeit erhält).

Dort können Sie sehen, dass ein strikter Gleichheitsvergleich (===) nur dann true zurückgibt, wenn Typ und Inhalt übereinstimmen

var f = "-1" === -1; //false

Während der abstrakte Gleichheitsvergleich (==) nur den Inhalt * überprüft, indem er Typen konvertiert und sie dann streng vergleicht:

var t = "-1" == -1; //true

Obwohl es ohne Rücksprache mit ECMA nicht klar ist , was JavaScript beim Vergleich berücksichtigt, wird der folgende Code als wahr ausgewertet.

 var howAmISupposedToKnowThat = [] == false; //true
MVCDS
quelle