In JavaScript schrieb Douglas Crockford:
JavaScript hat zwei Gruppen von Gleichheitsoperatoren:
===
und!==
und ihre bösen Zwillinge==
und!=
. Die Guten arbeiten so, wie Sie es erwarten würden. Wenn die beiden Operanden vom selben Typ sind und denselben Wert haben, dann===
produzierttrue
und!==
produziertfalse
. Die bösen Zwillinge tun das Richtige, wenn die Operanden vom selben Typ sind, aber wenn sie von unterschiedlichem Typ sind, versuchen sie, die Werte zu erzwingen. Die Regeln, nach denen sie das tun, sind kompliziert und nicht zu merken. Dies sind einige der interessanten Fälle:'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
Der Mangel an Transitivität ist alarmierend. Mein Rat ist, niemals die bösen Zwillinge zu benutzen. Verwenden Sie stattdessen immer
===
und!==
. Alle soeben gezeigten Vergleiche ergebenfalse
mit dem===
Bediener.
Gibt es in Anbetracht dieser eindeutigen Beobachtung jemals einen Zeitpunkt, zu dem die Verwendung ==
tatsächlich angemessen sein könnte?
quelle
==
standardmäßig verwendet werden, fällt===
mir auf, dass gerade etwas Wichtiges passiert.Antworten:
Ich werde dafür ein Argument vorbringen
==
Douglas Crockford, den Sie zitiert haben, ist bekannt für seine vielen und oft sehr nützlichen Meinungen. Obwohl ich in diesem speziellen Fall mit Crockford zusammen bin, ist es erwähnenswert, dass dies nicht die einzige Meinung ist. Es gibt andere wie den Sprachschöpfer Brendan Eich, bei denen das große Problem nicht auffällt
==
. Das Argument sieht ungefähr so aus:JavaScript ist eine Verhaltenssprache. Dinge werden basierend auf dem behandelt, was sie können und nicht auf ihrem tatsächlichen Typ. Aus diesem Grund können Sie die
.map
Methode eines Arrays in einer NodeList oder in einem jQuery-Auswahlsatz aufrufen. Das ist auch der Grund, warum Sie3 - "5"
etwas Sinnvolles tun und zurückbekommen können - weil "5" wie eine Zahl wirken kann.Wenn Sie eine
==
Gleichheit ausführen, vergleichen Sie den Inhalt einer Variablen und nicht ihren Typ . Hier sind einige Fälle, in denen dies nützlich ist:.value
eines Eingabeelements im DOM? Kein Problem! Sie müssen nicht damit beginnen, es zu gießen oder sich über seine Art Gedanken zu machen - Sie können==
es sofort zu Zahlen verarbeiten und etwas Sinnvolles zurückbekommen.== null
es, da verhaltensbezogennull
nichts da ist und undefiniert auch nichts da ist.==
Argument falsch ist. Es werden Fälle behandelt, in denen der Benutzer nichts oder nur Leerzeichen für Sie eingegeben hat. Dies ist wahrscheinlich das, was Sie benötigen.Schauen wir uns Crockfords Beispiele an und erklären wir sie wie folgt:
Grundsätzlich
==
ist es so konzipiert, dass es darauf basiert, wie sich Primitive in JavaScript verhalten, und nicht darauf, was sie sind . Ich persönlich bin mit diesem Standpunkt zwar nicht einverstanden, aber es ist auf jeden Fall sinnvoll, dies zu tun - insbesondere, wenn Sie dieses Paradigma der Behandlung von Typen auf der Grundlage von Verhaltensweisen in der gesamten Sprache anwenden.* Einige mögen die häufigere strukturelle Typisierung des Namens bevorzugen, aber es gibt einen Unterschied - sie ist nicht wirklich daran interessiert, den Unterschied hier zu diskutieren.
quelle
==
ist nicht, dass keiner der Vergleiche nützlich ist , sondern dass die Regeln unmöglich zu merken sind, sodass Sie fast garantiert Fehler machen. Zum Beispiel: "Müssen Sie überprüfen, ob Sie eine aussagekräftige Eingabe von einem Benutzer erhalten haben?", Aber "0" ist eine aussagekräftige Eingabe und'0'==false
wahr. Wenn Sie verwendet===
hätten, hätten Sie explizit darüber nachdenken müssen, und Sie hätten den Fehler nicht gemacht.3 - "5"
Beispiel selbst bringt einen guten Punkt auf den Punkt: Auch wenn Sie es ausschließlich===
zum Vergleich verwenden, funktionieren Variablen in Javascript genau so. Es gibt keine Möglichkeit, sich dem vollständig zu entziehen.==
eigenen Code, finde ihn als Anti-Pattern und stimme voll und ganz zu, dass der abstrakte Gleichheitsalgorithmus schwer zu merken ist. Was ich hier mache, ist das Argument, das die Leute dafür vorbringen.Es stellt sich heraus, dass jQuery das Konstrukt verwendet
ausführlich, als Abkürzung für den entsprechenden Code:
Dies ist eine Konsequenz der ECMAScript-Sprachspezifikation § 11.9.3, Der abstrakte Gleichheitsvergleichsalgorithmus , in der unter anderem angegeben ist, dass
und
Diese besondere Technik ist häufig genug , dass JSHint eine hat Flagge speziell dafür.
quelle
== null or undefined
ist der einzige Ort, an dem ich nicht benutze===
oder!==
==
und nur===
in Fällen verwendet wird, in denen dies erforderlich ist: github.com/madrobby/zepto/blob/master/src/event.js__proto__
, dass es fast im Alleingang in die Sprachspezifikation aufgenommen wurde, um zu verhindern, dass mobile Websites beschädigt werden .Das Überprüfen von Werten auf
null
oderundefined
ist eine Sache, wie bereits ausführlich erläutert wurde.Es gibt eine andere Sache, wo es
==
glänzt:Sie können Vergleiche
>=
wie folgt definieren (die Leute beginnen normalerweise mit,>
aber ich finde das eleganter):a > b
<=>a >= b && !(b >= a)
a == b
<=>a >= b && b >= a
a < b
unda <= b
werden als Übung dem Leser überlassen.Wie wir wissen, in JavaScript
"3" >= 3
und"3" <= 3
, von denen Sie erhalten3 == "3"
. Sie können darauf hinweisen, dass es eine schreckliche Idee ist, einen Vergleich zwischen Zeichenfolgen und Zahlen zuzulassen, indem Sie die Zeichenfolge analysieren. Aber da dies ist der Weg , es funktioniert,==
ist absolut der richtige Weg , diese Beziehung Operator zu implementieren.Das wirklich Gute daran
==
ist, dass es mit allen anderen Beziehungen konsistent ist. Anders ausgedrückt, wenn Sie folgendes schreiben:Sie verwenden implizit
==
bereits.Nun zu der ziemlich verwandten Frage: War es eine schlechte Wahl, Zahlen- und Zeichenfolgenvergleich so zu implementieren, wie er implementiert ist? Für sich genommen scheint es ziemlich dumm zu sein. Aber im Zusammenhang mit anderen Teilen von JavaScript und dem DOM ist es relativ pragmatisch, wenn man bedenkt, dass:
Object
eine Karte mit geringer Dichte von Ints zu Werten verwenden).input[type=number]
)Aus einer ganzen Reihe von Gründen war es sinnvoll, Zeichenfolgen bei Bedarf wie Zahlen zu verhalten. Und unter der Annahme, dass der Zeichenfolgenvergleich und die Zeichenfolgenverkettung unterschiedliche Operatoren haben (z. B.
::
für die Verkettung und eine Vergleichsmethode (bei der Sie alle möglichen Parameter in Bezug auf die Groß- und Kleinschreibung verwenden können und was nicht)), wäre dies in der Tat weniger verwirrend. Aber diese Überladung des Operators ist wahrscheinlich in der Tat, wo das "Java" in "JavaScript" herkommt;)quelle
>=
ist nicht wirklich transitiv. Es ist durchaus möglich, dass in JS wedera > b
nocha < b
nochb == a
(zum Beispiel:)NaN
.+
nicht wirklich kommutativ, weilNaN + 5 == NaN + 5
es nicht hält. Der Punkt ist, dass>=
mit Zahlenwerten gearbeitet wird, für die==
konsistent gearbeitet wird. Es sollte nicht überraschen, dass "keine Zahl" von Natur aus keine Zahl ist;)==
stimmt also mit dem schlechten Verhalten von überein>=
? Großartig, jetzt wünschte ich, es gäbe eine>==
...>=
eher mit dem der restlichen Sprach- / Standard-APIs überein. In seiner Gesamtheit schafft es JavaScript, mehr als die Summe seiner schrulligen Teile zu sein. Wenn Sie eine>==
möchten, möchten Sie auch eine strenge+
? In der Praxis erleichtern viele dieser Entscheidungen vieles. Also würde ich mich nicht beeilen, sie als arm zu beurteilen.>=
es Sinn macht,==
ist es ebenso Sinn - das ist alles. Niemand sagt , sollten Sie vergleichen[[]]
mitfalse
. In Sprachen wie C ist das Ergebnis dieser Unsinnsstufe undefiniertes Verhalten. Behandle es einfach genauso: Tu es nicht. Und es wird dir gut gehen. Sie müssen sich auch keine magischen Regeln merken. Und dann ist es eigentlich ziemlich einfach.Als professioneller Mathematiker sehe ich in Javscripts Gleichheitsoperator
==
(auch als "abstrakter Vergleich", "lose Gleichheit" bezeichnet ) einen Versuch, eine Äquivalenzbeziehung zwischen Entitäten aufzubauen , die Reflexivität , Symmetrie und Transitivität einschließt . Leider schlagen zwei dieser drei grundlegenden Eigenschaften fehl:==
ist nicht reflexiv :A == A
kann falsch sein, z==
ist nicht transitiv :A == B
undB == C
zusammen nicht implizierenA == C
, zNur symmetrische Eigenschaften bleiben erhalten:
A == B
impliziertB == A
, welcher Verstoß wohl auf jeden Fall undenkbar ist und zu ernsthaften Aufständen führen würde;)Warum sind Äquivalenzbeziehungen wichtig?
Denn das ist die wichtigste und häufigste Art von Beziehung, die durch zahlreiche Beispiele und Anwendungen gestützt wird. Die wichtigste Anwendung ist die Zerlegung von Entitäten in Äquivalenzklassen , was selbst eine sehr bequeme und intuitive Art ist, Beziehungen zu verstehen. Und das Versäumnis, Äquivalenz zu sein, führt zum Fehlen von Äquivalenzklassen, was wiederum zu einem Mangel an Intuitivität und unnötiger Komplexität führt, die allgemein bekannt sind.
Warum ist es so schrecklich,
==
für eine Nichtäquivalenzbeziehung zu schreiben ?Weil es unsere Vertrautheit und Intuition bricht, ist buchstäblich jedes interessante Verhältnis von Ähnlichkeit, Gleichheit, Kongruenz, Isomorphismus, Identität usw. eine Äquivalenz.
Typkonvertierung
Anstatt sich auf eine intuitive Äquivalenz zu verlassen, führt JavaScript die Typkonvertierung ein:
Aber wie ist die Typkonvertierung definiert? Über eine Reihe komplizierter Regeln mit zahlreichen Ausnahmen?
Versuch, eine Äquivalenzbeziehung aufzubauen
Boolesche. Klar
true
undfalse
sind nicht gleich und sollten in verschiedenen Klassen sein.Zahlen. Glücklicherweise ist die Gleichheit von Zahlen bereits klar definiert, wobei zwei verschiedene Zahlen niemals in derselben Äquivalenzklasse liegen. In der Mathematik ist das so. In JavaScript ist der Begriff der Zahl etwas deformiert durch Anwesenheit des exotischere
-0
,Infinity
und-Infinity
. Unsere mathematische Intuition schreibt vor, dass0
und-0
sollte in der gleichen Klasse sein (in der Tat-0 === 0
isttrue
), während jede der Unendlichkeiten eine eigene Klasse ist.Zahlen und Boolesche Werte. Wo setzen wir angesichts der Zahlenklassen Boolesche Werte?
false
wird ähnlich0
, wohingegentrue
wird ähnlich,1
aber keine andere Zahl:Gibt es hier eine Logik, mit der man sich
true
zusammensetzen kann1
? Zugegebenermaßen1
wird unterschieden, aber so ist-1
. Ich persönlich sehe keinen Grund , zu konvertierentrue
zu1
.Und es wird noch schlimmer:
Also
true
wird in der Tat in1
unter allen Zahlen umgerechnet ! Ist es logisch Ist es intuitiv? Die Antwort bleibt als Übung;)Aber was ist damit:
Der einzige boolean
x
mitx && true
Wesentrue
istx = true
. Was beweist, dass beide1
und2
(und jede andere Zahl als0
) konvertieren zutrue
! Was es zeigt, ist, dass unsere Konvertierung eine andere wichtige Eigenschaft versagt - die Bijektion . Dies bedeutet, dass zwei verschiedene Entitäten in dieselbe konvertiert werden können. Was für sich genommen kein großes Problem sein muss. Das große Problem entsteht, wenn wir diese Umrechnung verwenden, um ein Verhältnis von "Gleichheit" oder "loser Gleichheit" zu beschreiben, wie immer wir es nennen wollen. Aber eines ist klar - es wird keine Äquivalenzbeziehung sein und es wird nicht intuitiv über Äquivalenzklassen beschrieben.Aber können wir es besser machen?
Zumindest mathematisch - auf jeden Fall ja! Eine einfache Äquivalenzbeziehung zwischen booleans und Zahlen kann mit nur konstruiert werden
false
und0
in der gleichen Klasse. Dasfalse == 0
wäre also die einzige nicht triviale lose Gleichheit.Was ist mit Streichern?
Wir können Zeichenfolgen von Leerzeichen am Anfang und am Ende abschneiden, um sie in Zahlen umzuwandeln, und wir können Nullen vor ihnen ignorieren:
Wir erhalten also eine einfache Regel für einen String - schneiden Sie die Leerzeichen und Nullen vor. Entweder erhalten wir eine Zahl oder eine leere Zeichenkette. In diesem Fall konvertieren wir in diese Zahl oder Null. Oder wir bekommen keine Zahl, in diesem Fall konvertieren wir nicht und bekommen keine neue Beziehung.
Auf diese Weise könnten wir tatsächlich eine perfekte Äquivalenzbeziehung für die Gesamtmenge der Booleschen Werte, Zahlen und Zeichenfolgen erhalten! Ausgenommen, dass ... JavaScript-Designer offensichtlich eine andere Meinung haben:
Die beiden Zeichenfolgen, in die beide konvertieren,
0
sind also plötzlich nicht mehr ähnlich! Warum oder warum? Nach der Regel sind Strings genau dann lose gleich, wenn sie genau gleich sind! Diese Regel bricht nicht nur die Transitivität, wie wir sehen, sondern ist auch überflüssig! Was bringt es, einen anderen Operator==
zu erstellen , um ihn genau mit dem anderen Operator zu identisch zu machen===
?Fazit
Der lose Gleichheitsoperator
==
hätte sehr nützlich sein können, wenn er sich an einige grundlegende mathematische Gesetze gehalten hätte. Aber wie es leider nicht tut, leidet seine Nützlichkeit.quelle
NaN
? Sofern für den Vergleich mit Zeichenfolgen kein bestimmtes Zahlenformat erzwungen wird, muss dies entweder zu einem nicht intuitiven Zeichenfolgenvergleich oder zu einer Nicht-Transitivität führen.NaN
tritt als schlechter Bürger auf :-). Die Transivität kann und sollte für jeden Äquivalenzvergleich aufrechterhalten werden, ob intuitiv oder nicht.Ja, ich habe einen Anwendungsfall dafür gefunden, nämlich wenn Sie einen Schlüssel mit einem numerischen Wert vergleichen:
Ich denke, es ist viel natürlicher, den Vergleich
key == some_number
als alsNumber(key) === some_number
oder als durchzuführenkey === String(some_number)
.quelle
Ich bin heute auf eine ziemlich nützliche Anwendung gestoßen. Wenn Sie aufgefüllte Zahlen wie
01
normale Ganzzahlen vergleichen möchten ,==
funktioniert dies einwandfrei. Zum Beispiel:Es erspart Ihnen das Entfernen der 0 und die Umwandlung in eine Ganzzahl.
quelle
'04'-0 === 4
, oder vielleichtparseInt('04', 10) === 4
+'01' === 1
'011' == 011 // false
im nicht strengen Modus und SyntaxError im strengen Modus. :)Ich weiß, dass dies eine späte Antwort ist, aber es scheint einige mögliche Verwirrung darüber zu geben,
null
undundefined
welche IMHO das==
Böse macht , umso mehr, als der Mangel an Transitivität, der schlimm genug ist. Erwägen:Was bedeuten diese?
p1
hat einen Vorgesetzten, dessen Name "Alice" ist.p2
hat einen Supervisor, dessen Name "None" ist.p3
hat ausdrücklich, eindeutig, keinen Vorgesetzten .p4
kann oder kann einen Vorgesetzten haben. Wir wissen es nicht, es ist uns egal, wir sollten es nicht wissen (Datenschutzfrage?), Da es uns nichts angeht.Wenn Sie verwenden
==
, sind Sie in Konflikt geratennull
undundefined
das ist völlig unangemessen. Die beiden Begriffe bedeuten ganz verschiedene Dinge! Zu sagen, dass ich keinen Vorgesetzten habe, nur weil ich mich geweigert habe zu sagen, wer mein Vorgesetzter ist, ist falsch!Ich verstehe , gibt es Programmierer , die nicht über diesen Unterschied kümmern zwischen
null
undundefined
oder wählen Sie diese Begriffe anders zu verwenden. Und wenn Ihre Welt nicht verwendetnull
undundefined
richtig, oder Sie mögen Ihre eigene Interpretation zu diesen Bedingungen geben, soll es so sein. Das halte ich allerdings nicht für eine gute Idee.Übrigens , ich habe kein Problem damit
null
undundefined
beide sind falsch! Es ist vollkommen in Ordnung zu sagenund dann
null
undundefined
würde dazu führen, dass der Code, der den Supervisor verarbeitet, übersprungen wird. Das ist richtig, weil wir keinen Vorgesetzten kennen oder haben. Alles gut. Aber die beiden Situationen sind nicht gleich . Deshalb==
ist falsch. Auch hier können die Dinge falsch sein und im Enten-Tipp-Sinne verwendet werden, was sich hervorragend für dynamische Sprachen eignet. Es ist richtig, JavaScript, Pythonic, Rubyish, etc. Aber auch diese Dinge sind nicht gleich.Und verstehen Sie mich auf nicht-Transitivität nicht gestartet:
"0x16" == 10
,10 == "10"
aber nicht"10" == "0x16"
. Ja, JavaScript ist schwach typisiert. Ja, es ist Zwang. Aber Zwang sollte niemals für Gleichheit gelten.Crockford hat übrigens starke Meinungen. Aber weißt du was? Er ist hier richtig!
FWIW Ich verstehe, dass es Situationen gibt und ich persönlich darauf gestoßen bin, in denen
==
es zweckmäßig ist! Als würde man eine Zeichenfolge für Zahlen nehmen und beispielsweise mit 0 vergleichen. Dies ist jedoch Hack. Sie haben Bequemlichkeit als Kompromiss für ein ungenaues Modell der Welt.TL; DR: Falschheit ist ein großartiges Konzept. Es sollte sich nicht auf die Gleichstellung erstrecken.
quelle
p5
... die einzige Situation, in der dertypeof(p5.supervisor) === typeof(undefined)
Supervisor nicht einmal als Konzept existiert: D