JavaScript bietet drei verschiedene Wertvergleichsoperationen:
===
â strikte Gleichheit (drei Gleichheitszeichen)==
â lose Gleichheit (zwei Gleichheitszeichen)Object.is()
Welche Operation Sie wählen, hängt davon ab, welche Art von Vergleich Sie durchführen möchten. Kurz gesagt:
==
) führt eine Typkonvertierung durch, wenn zwei Dinge verglichen werden, und behandelt NaN
, -0
und +0
speziell, um den IEEE 754 zu entsprechen (also NaN != NaN
, und -0 == +0
);===
) führt den gleichen Vergleich wie das doppelte Gleichheitszeichen aus (einschlieÃlich der speziellen Behandlung für NaN
, -0
und +0
), jedoch ohne Typkonvertierung; wenn die Typen unterschiedlich sind, wird false
zurückgegeben.Object.is()
führt keine Typkonvertierung und keine spezielle Behandlung für NaN
, -0
und +0
durch (wodurch es das gleiche Verhalten wie ===
hat, auÃer bei diesen speziellen numerischen Werten).Diese entsprechen drei von vier Gleichheitsalgorithmen in JavaScript:
==
===
Object.is()
Beachten Sie, dass die Unterschiede zwischen diesen sich auf den Umgang mit Primärtypen beziehen; keine von ihnen vergleicht, ob die Parameter strukturell ähnlich sind. Für nicht-primitive Objekte x
und y
, die die gleiche Struktur haben, aber unterschiedliche Objekte sind, werden alle oben genannten Formen zu false
ausgewertet.
Strikte Gleichheit vergleicht zwei Werte auf Gleichheit. Keiner der Werte wird implizit in einen anderen Wert konvertiert, bevor der Vergleich erfolgt. Wenn die Werte unterschiedliche Typen haben, werden die Werte als ungleich angesehen. Wenn die Werte den gleichen Typ haben, keine Zahlen sind und den gleichen Wert haben, werden sie als gleich angesehen. SchlieÃlich, wenn beide Werte Zahlen sind, werden sie als gleich angesehen, wenn beide nicht NaN
sind und den gleichen Wert haben, oder wenn einer +0
und einer -0
ist.
const num = 0;
const obj = new String("0");
const str = "0";
console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true
console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
Strikte Gleichheit ist fast immer die richtige Vergleichsoperation. Für alle Werte auÃer Zahlen verwendet sie die offensichtliche Semantik: Ein Wert ist nur sich selbst gleich. Für Zahlen verwendet sie leicht unterschiedliche Semantik, um zwei verschiedene Randfälle abzudecken. Der erste ist, dass Gleitkommazahlen entweder positiv oder negativ unterzeichnet sein können. Dies ist nützlich, um bestimmte mathematische Lösungen darzustellen, aber da die meisten Situationen keinen Unterschied zwischen +0
und -0
machen, behandelt die strikte Gleichheit sie als den gleichen Wert. Der zweite ist, dass Gleitkommazahlen das Konzept eines ânicht-zahlâ-Werts (NaN
) enthalten, um die Lösung bestimmter unbestimmter mathematischer Probleme darzustellen: negative Unendlichkeit addiert zu positiver Unendlichkeit, zum Beispiel. Strikte Gleichheit behandelt NaN
als ungleich zu jedem anderen Wert â einschlieÃlich sich selbst. (Der einzige Fall, in dem (x !== x)
true
ist, ist, wenn x
NaN
ist.)
Neben ===
wird strikte Gleichheit auch von Methoden zur Array-Indexsuche verwendet, einschlieÃlich Array.prototype.indexOf()
, Array.prototype.lastIndexOf()
, TypedArray.prototype.indexOf()
, TypedArray.prototype.lastIndexOf()
und bei case
-Vergleich. Das bedeutet, dass Sie indexOf(NaN)
nicht verwenden können, um den Index eines NaN
-Werts in einem Array zu finden, oder NaN
als case
-Wert in einer switch
-Anweisung verwenden können, um es zu etwas zu machen, das übereinstimmt.
console.log([NaN].indexOf(NaN)); // -1
switch (NaN) {
case NaN:
console.log("Surprise"); // Nothing is logged
}
Lose Gleichheit mit ==
Lose Gleichheit ist symmetrisch: A == B
hat immer identische Semantik wie B == A
für beliebige Werte von A
und B
(auÃer für die Reihenfolge der angewandten Konvertierungen). Das Verhalten bei der Durchführung der losen Gleichheit mit ==
ist wie folgt:
true
nur zurück, wenn beide Operanden dasselbe Objekt referenzieren.true
nur zurück, wenn beide Operanden dieselben Zeichen in derselben Reihenfolge haben.true
nur zurück, wenn beide Operanden denselben Wert haben. +0
und -0
werden als derselbe Wert behandelt. Wenn einer der Operanden NaN
ist, geben Sie false
zurück; daher ist NaN
niemals gleich NaN
.true
nur zurück, wenn beide Operanden true
oder beide false
sind.true
nur zurück, wenn beide Operanden denselben Wert haben.true
nur zurück, wenn beide Operanden dasselbe Symbol referenzieren.null
oder undefined
ist, muss der andere ebenfalls null
oder undefined
sein, um true
zurückzugeben. Andernfalls geben Sie false
zurück.false
zurück.true
wird in 1 konvertiert und false
wird in 0 konvertiert. Vergleichen Sie dann die beiden Operanden nochmals los.NaN
zurückgegeben, was garantiert, dass die Gleichheit false
ist.NaN
ist, geben Sie false
zurück.BigInt()
-Konstruktor. Bei einem Konvertierungsfehler geben Sie false
zurück.Traditionell und gemäà ECMAScript sind alle Primitiven und Objekte lose ungleich zu undefined
und null
. Aber die meisten Browser erlauben einer sehr engen Klasse von Objekten (insbesondere das document.all
-Objekt für jede Seite), in einigen Kontexten so zu agieren, als ob sie den Wert undefined
emulieren. Lose Gleichheit ist ein solcher Kontext: null == A
und undefined == A
werten auf true aus, wenn und nur dann A ein Objekt ist, das undefined
emuliert. In allen anderen Fällen ist ein Objekt niemals lose gleich undefined
oder null
.
In den meisten Fällen wird die Verwendung von loser Gleichheit nicht empfohlen. Das Ergebnis eines Vergleichs unter Verwendung strikter Gleichheit lässt sich leichter vorhersagen und kann aufgrund des Fehlens von Typkonversionen schneller ausgewertet werden.
Das folgende Beispiel demonstriert lose Gleichheitsvergleiche, die das Zahlen-Primitiv 0
, das BigInt-Primitiv 0n
, das String-Primitiv '0'
und ein Objekt, dessen toString()
-Wert '0'
ist, umfassen.
const num = 0;
const big = 0n;
const str = "0";
const obj = new String("0");
console.log(num == str); // true
console.log(big == num); // true
console.log(str == big); // true
console.log(num == obj); // true
console.log(big == obj); // true
console.log(str == obj); // true
Lose Gleichheit wird nur vom ==
-Operator verwendet.
Gleichheit mit demselben Wert bestimmt, ob zwei Werte in allen Kontexten funktional identisch sind. (Dieser Anwendungsfall zeigt eine Instanz des Liskovschen Substitutionsprinzips.) Ein Beispiel tritt auf, wenn versucht wird, eine unveränderliche Eigenschaft zu ändern:
// Add an immutable NEGATIVE_ZERO property to the Number constructor.
Object.defineProperty(Number, "NEGATIVE_ZERO", {
value: -0,
writable: false,
configurable: false,
enumerable: false,
});
function attemptMutation(v) {
Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
}
Object.defineProperty
wirft eine Ausnahme, wenn versucht wird, eine unveränderliche Eigenschaft zu ändern, tut aber nichts, wenn keine tatsächliche Ãnderung angefordert wird. Wenn v
-0
ist, wurde keine Ãnderung angefordert, und es wird kein Fehler ausgelöst. Intern wird der neu angegebene Wert beim Neudefinieren einer unveränderlichen Eigenschaft mit dem aktuellen Wert mithilfe der Gleichheit mit demselben Wert verglichen.
Gleichheit mit demselben Wert wird von der Object.is
-Methode bereitgestellt. Sie wird fast überall in der Sprache verwendet, wo ein Wert von gleichwertiger Identität erwartet wird.
Ãhnlich wie Gleichheit mit demselben Wert, aber +0 und -0 werden als gleich angesehen.
Gleichheit mit demselben Wert Null ist nicht als JavaScript-API verfügbar, kann jedoch mit benutzerdefiniertem Code implementiert werden:
function sameValueZero(x, y) {
if (typeof x === "number" && typeof y === "number") {
// x and y are equal (may be -0 and 0) or they are both NaN
return x === y || (x !== x && y !== y);
}
return x === y;
}
Gleichheit mit demselben Wert Null unterscheidet sich von der strikten Gleichheit dadurch, dass NaN
als gleichwertig angesehen wird, und von der Gleichheit mit demselben Wert dadurch, dass -0
als gleichwertig zu 0
angesehen wird. Dies führt dazu, dass es in der Regel das sinnvollste Verhalten während der Suche aufweist, besonders wenn mit NaN
gearbeitet wird. Es wird von Array.prototype.includes()
, TypedArray.prototype.includes()
sowie von Methoden von Map
und Set
zur Prüfung der Schlüsselgleichheit verwendet.
Menschen vergleichen oft doppeltes und dreifaches Gleichheitszeichen, indem sie sagen, eines sei eine "erweiterte" Version des anderen. Zum Beispiel könnte man sagen, dass das doppelte Gleichheitszeichen eine erweiterte Version des dreifachen Gleichheitszeichens ist, da das erstere alles tut, was das letztere tut, aber mit Typkonvertierung auf seinen Operanden â zum Beispiel 6 == "6"
. Alternativ kann behauptet werden, dass das doppelte Gleichheitszeichen die Grundlinie ist und das dreifache Gleichheitszeichen eine Erweiterung darstellt, da es die beiden Operanden erfordert, denselben Typ zu haben, was eine zusätzliche Einschränkung darstellt.
Diese Denkweise impliziert jedoch, dass die Gleichheitsvergleiche ein eindimensionales "Spektrum" bilden, bei dem "völlig strikt" am einen Ende und "völlig lose" am anderen liegt. Dieses Modell versagt bei Object.is
, da es weder "lockerer" als das doppelte Gleichheitszeichen noch "strikter" als das dreifache Gleichheitszeichen ist, noch dass es irgendwo dazwischen passt (d.h. sowohl strikter als das doppelte Gleichheitszeichen, aber lockerer als das dreifache Gleichheitszeichen). Wir können aus der Vergleichstabelle unten sehen, dass dies auf die Weise zurückzuführen ist, wie Object.is
NaN
behandelt. Beachten Sie, dass wenn Object.is(NaN, NaN)
auf false
ausgewertet würde, wir sagen könnten, dass es auf dem losen/strikten Spektrum als eine noch striktere Form des dreifachen Gleichheitszeichens passt, eine die zwischen -0
und +0
unterscheidet. Die NaN
-Behandlung bedeutet jedoch, dass dies nicht wahr ist. Leider muss Object.is
in Bezug auf seine spezifischen Merkmale betrachtet werden, anstatt in Bezug auf seine Laxheit oder Strenge gegenüber den Gleichheitsoperatoren.
==
===
Object.is
SameValueZero
undefined
undefined
â
true
â
true
â
true
â
true
null
null
â
true
â
true
â
true
â
true
true
true
â
true
â
true
â
true
â
true
false
false
â
true
â
true
â
true
â
true
'foo'
'foo'
â
true
â
true
â
true
â
true
0
0
â
true
â
true
â
true
â
true
+0
-0
â
true
â
true
â false
â
true
+0
0
â
true
â
true
â
true
â
true
-0
0
â
true
â
true
â false
â
true
0n
-0n
â
true
â
true
â
true
â
true
0
false
â
true
â false
â false
â false
""
false
â
true
â false
â false
â false
""
0
â
true
â false
â false
â false
'0'
0
â
true
â false
â false
â false
'17'
17
â
true
â false
â false
â false
[1, 2]
'1,2'
â
true
â false
â false
â false
new String('foo')
'foo'
â
true
â false
â false
â false
null
undefined
â
true
â false
â false
â false
null
false
â false
â false
â false
â false
undefined
false
â false
â false
â false
â false
{ foo: 'bar' }
{ foo: 'bar' }
â false
â false
â false
â false
new String('foo')
new String('foo')
â false
â false
â false
â false
0
null
â false
â false
â false
â false
0
NaN
â false
â false
â false
â false
'foo'
NaN
â false
â false
â false
â false
NaN
NaN
â false
â false
â
true
â
true
Wann sollte man Object.is() gegenüber dreifachem Gleichheitszeichen verwenden
Im Allgemeinen ist die einzige Zeit, in der Object.is
's spezielles Verhalten gegenüber Nullen von Interesse sein könnte, bei der Verfolgung bestimmter Metaprogrammierungsschemata, insbesondere im Hinblick auf Eigenschaftsbeschreibungen, wenn es wünschenswert ist, dass Ihre Arbeit einige der Eigenschaften von Object.defineProperty
spiegelt. Wenn Ihr Anwendungsfall dies nicht erfordert, wird vorgeschlagen, Object.is
zu vermeiden und stattdessen ===
zu verwenden. Auch wenn Ihre Anforderungen den Vergleich zwischen zwei NaN
-Werten erfordern, die zu true
auswerten, ist es im Allgemeinen einfacher, die NaN
-Prüfungen (unter Verwendung der isNaN
-Methode aus früheren Versionen von ECMAScript) spezifisch zu behandeln, als herauszufinden, wie umliegende Berechnungen das Vorzeichen von Nullen, die Sie in Ihrem Vergleich antreffen, beeinflussen könnten.
Hier ist eine nicht erschöpfende Liste von eingebauten Methoden und Operatoren, die dazu führen könnten, dass sich eine Unterscheidung zwischen -0
und +0
in Ihrem Code manifestiert:
-
(Unäres Negieren)
Betrachten Sie das folgende Beispiel:
const stoppingForce = obj.mass * -obj.velocity;
Wenn obj.velocity
0
(oder zu 0
berechnet wird) ist, wird an dieser Stelle ein -0
eingeführt und breitet sich in stoppingForce
aus.
Math.atan2
, Math.ceil
, Math.pow
, Math.round
In einigen Fällen ist es möglich, dass ein -0
in einen Ausdruck als Rückgabewert dieser Methoden eingeführt wird, auch wenn kein -0
als einer der Parameter vorhanden ist. Zum Beispiel wird Math.pow()
verwendet, um -Infinity
auf eine beliebige negativen ungerade Exponenten zu erheben, wertet zu -0
. Konsultieren Sie die Dokumentation für die einzelnen Methoden.
Math.floor
, Math.max
, Math.min
, Math.sin
, Math.sqrt
, Math.tan
Es ist möglich, -0
als Rückgabewert aus diesen Methoden in einigen Fällen zu erhalten, bei denen ein -0
als einer der Parameter existiert. Z.B. Math.min(-0, +0)
wertet zu -0
aus. Konsultieren Sie die Dokumentation für die einzelnen Methoden.
~
, <<
, >>
Jeder dieser Operatoren verwendet intern den ToInt32-Algorithmus. Da es in der internen 32-Bit-Integer-Implementierung nur eine Darstellung für 0 gibt, überlebt -0
nicht eine Rundreise nach einer inversen Operation. Z.B. Object.is(~~(-0), -0)
und Object.is(-0 << 2 >> 2, -0)
werten beide zu false
aus.
Sich auf Object.is
zu verlassen, wenn das Vorzeichen von Nullen nicht berücksichtigt wird, kann gefährlich sein. Natürlich erfüllt es genau die gewünschte Funktion, wenn die Absicht darin besteht, zwischen -0
und +0
zu unterscheiden.
Die Object.is
-Spezifikation behandelt alle Instanzen von NaN
als dasselbe Objekt. Seit Typed Arrays jedoch verfügbar sind, können wir unterschiedliche Gleitkommadarstellungen von NaN
haben, die sich nicht in allen Kontexten identisch verhalten. Zum Beispiel:
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// Get a byte representation of NaN
const n = f2b(NaN);
// Change the first bit, which is the sign bit and doesn't matter for NaN
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
Siehe auch
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4