
Unabhängig von Ihrer JavaScript-Programmiererfahrung verstehen Sie die Sprache höchstwahrscheinlich nicht vollständig. In diesem kurzen Handbuch werden Typen ausführlicher behandelt als in allen vorhandenen Büchern: Sie erfahren, wie Typen funktionieren, welche Probleme bei der Konvertierung auftreten und wie Sie neue Funktionen verwenden.
Wie andere Bücher in der
Reihe „Sie kennen JS nicht“ zeigt es nicht triviale Aspekte der Sprache, von denen JavaScript-Programmierer sich lieber fernhalten (oder davon ausgehen, dass sie nicht existieren). Mit diesem Wissen erreichen Sie eine echte JavaScript-Meisterschaft.
Auszug. Gleichheit ist streng und nicht streng.
Die nicht strikte Gleichheit wird vom Operator == und die strikte Gleichheit vom Operator === überprüft. Beide Operatoren werden verwendet, um zwei Werte für „Gleichheit“ zu vergleichen, aber die Wahl der Form (streng / nicht streng) führt zu sehr wichtigen Verhaltensunterschieden, insbesondere in der Art und Weise, wie die Entscheidung über Gleichheit getroffen wird.
Es gibt ein weit verbreitetes Missverständnis über diese beiden Operatoren: "== prüft auf Wertgleichheit und === prüft auf Gleichheit von Werten und Typen." Klingt vernünftig
aber ungenau. Unzählige seriöse JavaScript-Bücher und -Blogs sagen genau das, aber leider sind sie alle falsch.
Die korrekte Beschreibung lautet: "== ermöglicht die Typkonvertierung bei der Überprüfung der Gleichheit und === verbietet die Typkonvertierung."
Leistung zur Überprüfung der Gleichstellung
Halten Sie inne und überlegen Sie, wie sich die erste (ungenaue) Erklärung von der zweiten (genauen) unterscheidet.
In der ersten Erklärung scheint es offensichtlich, dass der Operator === mehr Arbeit leistet als ==, da er auch den Typ überprüfen muss.
In der zweiten Erklärung erledigt der Operator == mehr Arbeit, da er bei verschiedenen Typen eine Typkonvertierung durchlaufen muss.
Fallen Sie nicht in die Falle, in die viele fallen. Denken Sie nicht, dass dies die Geschwindigkeit des Programms beeinflusst und == wesentlich langsamer ist ===. Obwohl die Konvertierung einige Zeit in Anspruch nimmt, dauert es einige Mikrosekunden (ja, Millionstelsekunden).
Wenn Sie zwei Werte desselben Typs vergleichen, verwenden == und === denselben Algorithmus. Wenn Sie also kleine Unterschiede bei der Implementierung der Engine nicht berücksichtigen, müssen sie einen ausführen
und der gleiche Job.
Wenn Sie zwei Werte unterschiedlichen Typs vergleichen, ist die Leistung kein wichtiger Faktor. Sie müssen sich noch etwas fragen: Wenn ich zwei Werte vergleiche, möchte ich, dass die Typkonvertierung erfolgt oder nicht?
Wenn Sie eine Konvertierung benötigen, verwenden Sie nicht strikte Gleichheit ==, und wenn die Konvertierung unerwünscht ist, verwenden Sie strikte Gleichheit ===.
Beide Operatoren == und === überprüfen die Typen ihrer Operanden. Der Unterschied besteht darin, wie sie auf Typinkongruenzen reagieren.
Abstrakte Gleichstellungsprüfung
Das Verhalten des Operators == ist in Abschnitt 11.9.3 der ES5-Spezifikation („Abstract Equality Checker Algorithm“) definiert. Hier ist ein detaillierter, aber einfacher Algorithmus mit einer expliziten Auflistung aller möglichen Kombinationen von Typen und Typkonvertierungsmethoden (falls erforderlich), die in jeder Kombination angewendet werden sollten.
Wenn jemand die (implizite) Typkonvertierung als zu komplex verurteilt und zu viele Fehler für den praktischen Gebrauch enthält, verurteilt er die Regeln der "abstrakten Gleichheitsprüfung". Es wird normalerweise gesagt, dass dieser Mechanismus für das praktische Studium und die Verwendung zu kompliziert und unnatürlich ist und dass er Fehler in JS-Programmen erzeugt, anstatt das Lesen von Code zu vereinfachen.
Ich glaube, dass dies eine falsche Annahme ist - Sie Leser sind kompetente Entwickler, die den ganzen Tag über Algorithmen schreiben, dh Code (und ihn auch lesen und verstehen). Aus diesem Grund werde ich versuchen, den "abstrakten Gleichheitstest" in einfachen Worten zu erklären. Ich empfehle jedoch auch, Abschnitt 11.9.3 der ES5-Spezifikation zu lesen. Ich denke, es wird Sie überraschen, wie logisch alles da ist.
Tatsächlich heißt es im ersten Abschnitt (11.9.3.1), dass zwei verglichene Werte, die vom gleichen Typ sind, auf einfache und natürliche Weise verglichen werden. Zum Beispiel ist 42 nur 42 und die Zeichenfolge "abc" ist nur "abc".
Ein paar kleine Ausnahmen zu beachten:
- Der Wert von NaN ist niemals gleich sich selbst (siehe Kapitel 2).
- +0 und -0 sind gleich (siehe Kapitel 2).
Der letzte Abschnitt in Abschnitt 11.9.3.1 ist einem strengen Test der Gleichheit mit Objekten (einschließlich Funktionen und Arrays) gewidmet. Zwei solche Werte sind
nur dann gleich
, wenn sich beide auf genau
denselben Wert beziehen. Es wird keine Typkonvertierung durchgeführt.
Eine strikte Gleichheitsprüfung === ist identisch mit 11.9.3.1 definiert, einschließlich der Bereitstellung von zwei Objektwerten. Diese Tatsache ist sehr wenig bekannt, aber == und === verhalten sich beim Vergleich zweier Objekte völlig identisch!
Der Rest des Algorithmus in 11.9.3 zeigt an, dass nicht strenge Gleichheit == verwendet werden kann, um zwei verschiedene Arten von Werten zu vergleichen, von denen einer oder beide erforderlich sind
implizite Konvertierung. Durch die Konvertierung werden die Bezeichnungen auf einen Typ reduziert, wonach sie durch einfache Identität direkt auf Gleichheit verglichen werden können
Werte.
Die Operation einer schwachen Ungleichheitsprüfung! = Wird genau so bestimmt, wie man es erwarten würde; Tatsächlich ist die Operation == vollständig implementiert, gefolgt von einer Berechnung
Verweigerung des Ergebnisses. Gleiches gilt für die strikte Überprüfung der Ungleichung! ==.
Vergleich: Zeichenfolgen und Zahlen
Um die Konvertierung von == zu demonstrieren, erstellen Sie zunächst Beispiele für Zeichenfolgen und Zahlen, die weiter oben in diesem Kapitel ausgeführt wurden:
var a = 42; var b = "42"; a === b;
Wie erwartet schlägt die Prüfung a === b fehl, da die Konvertierung nicht zulässig ist und die Werte 42 und "42" unterschiedlich sind.
Im zweiten Vergleich a == b wird jedoch eine nicht strenge Gleichheit verwendet; Dies bedeutet, dass der Vergleichsalgorithmus bei unterschiedlichen Typen eine implizite Konvertierung von eins durchführt
oder beides.
Aber welche Art von Konvertierung wird hier durchgeführt? Wird der Wert a, dh 42, eine Zeichenfolge oder wird der Wert b „42“ eine Zahl? Die ES5-Spezifikation in den Abschnitten 11.9.3.4–5 lautet:
- Wenn Typ (x) vom Typ Nummer und Typ (y) vom Typ String ist, geben Sie das Ergebnis des Vergleichs x == ToNumber (y) zurück.
- Wenn Typ (x) vom Typ String und Typ (y) vom Typ Number ist, geben Sie das Ergebnis des Vergleichs ToNumber (x) == y zurück.
In der Spezifikation werden formale Namen der Typen Number und String verwendet, während im Buch für primitive Typen normalerweise die Notationsnummer und der String verwendet werden. Verwechseln Sie den Fall des Zahlensymbols in der Spezifikation nicht mit der integrierten Funktion Number (). Für unsere Zwecke spielt der Fall von Zeichen im Namen des Typs keine Rolle - sie bedeuten dasselbe.
Die Spezifikation besagt, dass der Wert "42" zum Vergleich in eine Zahl umgewandelt wird. Wie die Konvertierung durchgeführt wird, wurde bereits zuvor beschrieben, insbesondere bei der Beschreibung der abstrakten Operation ToNumber. In diesem Fall ist es ziemlich offensichtlich
dass die resultierenden zwei Werte von 42 gleich sind.
Vergleich: alles mit Booleschen Werten
Eine der gefährlichsten Fallen bei der impliziten Konvertierung vom Typ == tritt auf, wenn versucht wird, Werte direkt mit wahr oder falsch zu vergleichen.
Ein Beispiel:
var a = "42"; var b = true; a == b;
Warten Sie, was ist hier los? Wir wissen, dass „42“ die wahre Bedeutung ist (siehe weiter oben in diesem Kapitel). Wie stellt sich heraus, dass ein Vergleich mit true mit der Aussage der strengen Gleichheit ==
gibt nicht wahr?
Der Grund ist einfach und gleichzeitig täuschend gerissen. Es ist leicht zu missverstehen, viele JS-Entwickler bemühen sich nicht, es vollständig zu verstehen.
Wir zitieren noch einmal die Spezifikation, Abschnitte 11.9.3.6–7:
- Wenn Typ (x) vom Typ Boolean ist, geben Sie das Ergebnis des Vergleichs ToNumber (x) == y zurück.
- Wenn Typ (y) vom Typ Boolean ist, geben Sie das Ergebnis des Vergleichs x == ToNumber (y) zurück.
Mal sehen, was hier ist. Erster Schritt:
var x = true; var y = "42"; x == y;
Typ (x) gehört wirklich zum Booleschen Typ, daher wird die ToNumber (x) -Operation ausgeführt, die true in 1 konvertiert. Nun wird Bedingung 1 == "42" berechnet. Die Typen sind immer noch unterschiedlich, daher wiederholt sich der Algorithmus (fast rekursiv). Wie im vorherigen Fall wird "42" in 42 umgewandelt, und Bedingung 1 == 42 ist offensichtlich falsch.
Wenn Sie Operanden austauschen, bleibt das Ergebnis gleich:
var x = "42"; var y = false; x == y;
Diesmal ist Typ (y) vom Typ Boolean, daher gibt ToNumber (y) 0 an. Die Bedingung "42" == 0 wird rekursiv zu 42 == 0, was natürlich falsch ist.
Mit anderen Worten, der Wert "42" ist weder == wahr noch == falsch. Diese Aussage erscheint auf den ersten Blick völlig undenkbar. Wie kann Bedeutung weder wahr noch falsch sein?
Aber das ist das Problem! Sie stellen die falsche Frage. Obwohl es in der Tat nicht deine Schuld ist, ist es das Gehirn, das dich täuscht.
Der Wert "42" ist zwar wahr, aber die Konstruktion "42" == wahr führt überhaupt keinen Booleschen / Transformationstest durch, was auch immer Ihr Gehirn sagt. "42" konvertiert nicht in boolesch (true); Stattdessen wird true in 1 konvertiert und dann wird „42“ in 42 konvertiert.
Ob Sie es mögen oder nicht, ToBoolean wird hier überhaupt nicht verwendet, daher ist die Wahrheit oder Falschheit von „42“ für die == -Operation überhaupt nicht wichtig! Es ist wichtig zu verstehen, wie sich der == Vergleichsalgorithmus in allen verschiedenen Typkombinationen verhält. Wenn der boolesche Wert einseitig ist, wird er immer zuerst in eine Zahl konvertiert.
Wenn Ihnen das seltsam vorkommt, sind Sie nicht allein. Persönlich empfehle ich, unter keinen Umständen == true oder == false zu verwenden. Niemals.
Aber denken Sie daran, dass ich hier nur von == spreche. Die Konstruktionen === true und === false erlauben keine Typkonvertierung, daher sind sie vor der versteckten ToNumber-Konvertierung geschützt.
Ein Beispiel:
var a = "42";
Wenn Sie in Ihrem Code == true oder == false (lose Gleichheit mit Booleschen Werten) vermeiden, müssen Sie sich niemals um diese Wahrheits- / Falschheitsfalle sorgen.
Vergleich: null mit undefiniert
Ein weiteres Beispiel für eine implizite Konvertierung tritt auf, wenn Sie die lax == -Gleichheit zwischen null und undefinierten Werten verwenden. Ich werde wieder die ES5-Spezifikation zitieren,
Abschnitte 11.9.3.2–3:
- Wenn x null und y undefiniert enthält, geben Sie true zurück.
- Wenn x undefiniert und y null enthält, geben Sie true zurück.
Null und undefiniert im Vergleich zum nicht strengen Operator == sind gleich (dh sie werden ineinander konvertiert) und keine anderen Werte in der gesamten Sprache.
Für uns bedeutet dies, dass null und undefiniert zu Vergleichszwecken als nicht unterscheidbar angesehen werden können, wenn Sie den nicht strengen Gleichheitstestoperator == verwenden, der ihre gegenseitige implizite Konvertierung ermöglicht:
var a = null; var b; a == b;
Die Konvertierung zwischen null und undefiniert ist sicher und vorhersehbar, und kein anderer Wert kann für eine solche Prüfung falsch positive Ergebnisse liefern. Ich empfehle diese Konvertierung, damit sich null und undefined im Programm nicht unterscheiden und als ein einziger Wert interpretiert werden.
Ein Beispiel:
var a = doSomething(); if (a == null) {
Die Prüfung a == null wird nur bestanden, wenn doSomething () null oder undefiniert zurückgibt und für einen anderen Wert (einschließlich 0, false und "") fehlschlägt.
Die explizite Form dieser Prüfung, die solche Typkonvertierungen verbietet, sieht (meiner Meinung nach) viel hässlicher aus und funktioniert möglicherweise etwas weniger effizient!
var a = doSomething(); if (a === undefined || a === null) {
Ich glaube, dass die Form a == null ein weiteres Beispiel für eine Situation ist, in der eine implizite Konvertierung das Lesen des Codes erleichtert, dies jedoch zuverlässig und sicher tut.
Vergleich: Objekte und Nichtobjekte
Wenn ein Objekt / eine Funktion / ein Array mit einem einfachen skalaren Grundelement (Zeichenfolge, Zahl oder Boolescher Wert) verglichen wird, lautet die ES5-Spezifikation Folgendes (Abschnitt 11.9.3.8–9):
- Wenn Typ (x) vom Typ String oder Number und Typ (y) vom Typ Object ist, geben Sie das Ergebnis des Vergleichs x == ToPrimitive (y) zurück.
- Wenn Typ (x) vom Typ Objekt und Typ (y) vom Typ Zeichenfolge oder Zahl ist, geben Sie das Ergebnis des Vergleichs ToPrimitive (x) == y zurück.
Möglicherweise haben Sie bemerkt, dass in diesen Abschnitten der Spezifikation nur String und Number erwähnt werden, nicht jedoch Boolean. Tatsache ist, dass, wie oben erwähnt, in den Abschnitten 11.9.3.6–7 sichergestellt wird, dass jeder boolesche Operand zuerst als Zahl dargestellt wird.
Ein Beispiel:
var a = 42; var b = [ 42 ]; a == b;
Für den Wert [42] wird die abstrakte Operation ToPrimitive aufgerufen (siehe "Abstrakte Operationen"), die das Ergebnis "42" ergibt. Von diesem Moment an bleibt die einfache Bedingung „42“ == 42 bestehen, die sich, wie wir bereits herausgefunden haben, in 42 == 42 verwandelt, so dass a und b bis zur Typkonvertierung gleich sind.
Wie zu erwarten, sind auch in diesem Fall alle Funktionen der abstrakten ToPrimitive-Operation ((toString (), valueOf ())) anwendbar. Dies kann sehr nützlich sein, wenn Sie eine komplexe Datenstruktur haben und dies möchten Definieren Sie dafür eine spezielle Methode valueOf (), die einen einfachen Wert für die Überprüfung der Gleichheit bereitstellen muss.
In Kapitel 3 wurde das „Entpacken“ eines Objekt-Wrappers um einen Grundwert (wie z. B. in einem neuen String („abc“)) untersucht, was zur Rückgabe des zugrunde liegenden Grundelements führte
Wert ("abc"). Dieses Verhalten hängt mit der ToPrimitive-Transformation im == -Algorithmus zusammen:
var a = "abc"; var b = Object( a );
a == b gibt true an, weil b durch die ToPrimitive-Operation in den einfachen skalaren Grundprimitivwert "abc" konvertiert (oder "entpackt") wird, der dem Wert von a entspricht.
Es gibt einige Werte, für die dies aufgrund anderer übergeordneter Regeln im == Algorithmus nicht der Fall ist. Ein Beispiel:
var a = null; var b = Object( a );
Die Werte null und undefiniert können nicht gepackt werden (sie haben keinen äquivalenten Objekt-Wrapper), daher unterscheidet sich Object (null) nicht grundlegend von Object (): Beide Aufrufe erstellen das Übliche
ny Objekt.
NaN kann in den entsprechenden Number-Objekt-Wrapper gepackt werden. Wenn == jedoch das Entpacken verursacht, schlägt der NaN == NaN-Vergleich fehl, da der NaN-Wert niemals gleich sich selbst ist (siehe Kapitel 2).
»Weitere Informationen zum Buch finden Sie auf
der Website des Herausgebers»
Inhalt»
Auszug25% Rabatt auf Gutschein für Händler -
JavaScriptNach Bezahlung der Papierversion des Buches wird ein elektronisches Buch per E-Mail verschickt.