[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness [Back]  [Original]

Gleichheitsvergleiche und Wertgleichheit - JavaScript | MDN

Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.

View in English Always switch to English

Gleichheitsvergleiche und Wertgleichheit

JavaScript bietet drei verschiedene Operationen zum Vergleichen von Werten:

  • === strikte Gleichheit (dreifaches Gleichheitszeichen)
  • == lose Gleichheit (doppeltes Gleichheitszeichen)
  • Object.is()

Welche Operation Sie whlen, hngt davon ab, welche Art von Vergleich Sie durchfhren mchten. Kurz gesagt:

  • Das doppelte Gleichheitszeichen (==) fhrt beim Vergleich zweier Werte eine Typumwandlung durch und behandelt NaN, -0 und +0 gem IEEE 754 gesondert (sodass NaN != NaN und -0 == +0 gilt).
  • Das dreifache Gleichheitszeichen (===) fhrt denselben Vergleich wie das doppelte Gleichheitszeichen durch (einschlielich der Sonderbehandlung von NaN, -0 und +0), jedoch ohne Typumwandlung. Wenn sich die Typen unterscheiden, wird false zurckgegeben.
  • Object.is() fhrt weder eine Typumwandlung noch eine Sonderbehandlung von NaN, -0 und +0 durch (und verhlt sich damit auer bei diesen besonderen numerischen Werten wie ===).

Sie entsprechen drei der vier Gleichheitsalgorithmen in JavaScript:

Beachten Sie, dass die Unterschiede zwischen diesen Algorithmen ausschlielich die Behandlung primitiver Werte betreffen. Keiner von ihnen prft, ob die Parameter ihrer Struktur nach bereinstimmen. Fr zwei nicht primitive Objekte x und y, die dieselbe Struktur haben, aber unterschiedliche Objekte sind, ergeben alle oben genannten Vergleiche false.

Der rekursive Vergleich der Inhalte unterschiedlicher Objekte oder Arrays wird als tiefer Gleichheitsvergleich bezeichnet. JavaScript stellt keinen allgemeinen Operator fr tiefe Vergleiche bereit. Bibliotheken und Host-APIs knnen Vergleichsfunktionen mit unterschiedlichen Regeln bereitstellen.

In diesem Artikel

Strikte Gleichheit mit ===

Bei der strikten Gleichheit werden zwei Werte auf Gleichheit verglichen. Keiner der Werte wird vor dem Vergleich implizit in einen anderen Wert umgewandelt. Wenn die Werte unterschiedliche Typen haben, gelten sie als ungleich. Haben sie denselben Typ, sind keine Zahlen und haben denselben Wert, gelten sie als gleich. Sind beide Werte Zahlen, gelten sie schlielich als gleich, wenn keiner von ihnen NaN ist und sie denselben Wert haben oder wenn einer +0 und der andere -0 ist.

js
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. Fr alle Werte auer Zahlen verwendet sie die naheliegende Semantik: Ein Wert ist nur sich selbst gleich. Bei Zahlen verwendet sie eine leicht abweichende Semantik, um zwei Sonderflle auszuklammern. Erstens kann eine Gleitkomma-Null ein positives oder negatives Vorzeichen haben. Das ist zur Darstellung bestimmter mathematischer Lsungen ntzlich. Da der Unterschied zwischen +0 und -0 in den meisten Situationen jedoch keine Rolle spielt, behandelt die strikte Gleichheit beide als denselben Wert. Zweitens umfassen Gleitkommazahlen den Wert Not a Number, NaN, der das Ergebnis bestimmter nicht wohldefinierter mathematischer Probleme darstellt, beispielsweise die Addition von negativer und positiver Unendlichkeit. Bei strikter Gleichheit gilt NaN als ungleich zu jedem anderen Wert auch zu sich selbst. (Der einzige Fall, in dem (x !== x) den Wert true ergibt, ist, wenn x den Wert NaN hat.)

Neben === verwenden auch Methoden zur Suche nach Array-Indizes strikte Gleichheit, darunter Array.prototype.indexOf(), Array.prototype.lastIndexOf(), TypedArray.prototype.indexOf() und TypedArray.prototype.lastIndexOf(). Sie wird auch beim Abgleich von case-Werten verwendet. Das bedeutet, dass Sie mit indexOf(NaN) den Index eines NaN-Werts in einem Array nicht finden knnen. Ebenso kann NaN als case-Wert in einer switch-Anweisung mit keinem Wert bereinstimmen.

js
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 fr beliebige Werte von A und B immer dieselbe Semantik wie B == A (abgesehen von der Reihenfolge der angewendeten Umwandlungen). Der Vergleich mit == luft folgendermaen ab:

  1. Haben die Operanden denselben Typ, werden sie wie folgt verglichen:
    • Object: true wird nur zurckgegeben, wenn beide Operanden auf dasselbe Objekt verweisen.
    • String: true wird nur zurckgegeben, wenn beide Operanden dieselben Zeichen in derselben Reihenfolge enthalten.
    • Number: true wird nur zurckgegeben, wenn beide Operanden denselben Wert haben. +0 und -0 werden als derselbe Wert behandelt. Ist einer der Operanden NaN, wird false zurckgegeben; NaN ist also niemals gleich NaN.
    • Boolean: true wird nur zurckgegeben, wenn beide Operanden true oder beide false sind.
    • BigInt: true wird nur zurckgegeben, wenn beide Operanden denselben Wert haben.
    • Symbol: true wird nur zurckgegeben, wenn beide Operanden auf dasselbe Symbol verweisen.
  2. Ist einer der Operanden null oder undefined, muss auch der andere null oder undefined sein, damit true zurckgegeben wird. Andernfalls wird false zurckgegeben.
  3. Ist einer der Operanden ein Objekt und der andere ein primitiver Wert, wird das Objekt in einen primitiven Wert umgewandelt.
  4. An diesem Punkt sind beide Operanden primitive Werte (String, Number, Boolean, Symbol oder BigInt). Die weiteren Umwandlungen erfolgen je nach Fall:
    • Haben sie denselben Typ, werden sie wie in Schritt 1 verglichen.
    • Ist einer der Operanden ein Symbol und der andere nicht, wird false zurckgegeben.
    • Ist einer der Operanden ein Boolean und der andere nicht, wird der Boolean in eine Zahl umgewandelt: true wird zu 1 und false zu 0. Anschlieend werden die beiden Operanden erneut auf lose Gleichheit verglichen.
    • Number und String: Der String wird in eine Zahl umgewandelt. Schlgt die Umwandlung fehl, entsteht NaN, wodurch der Gleichheitsvergleich garantiert false ergibt.
    • Number und BigInt: Die Werte werden anhand ihres mathematischen Werts verglichen. Ist die Zahl Infinity oder NaN, wird false zurckgegeben.
    • String und BigInt: Der String wird mit demselben Algorithmus wie beim BigInt()-Konstruktor in einen BigInt umgewandelt. Schlgt die Umwandlung fehl, wird false zurckgegeben.

Nach herkmmlichem Verhalten und gem ECMAScript sind alle primitiven Werte und Objekte bei einem losen Vergleich ungleich undefined und null. Die meisten Browser lassen jedoch zu, dass sich eine sehr eng begrenzte Klasse von Objekten (genauer gesagt das document.all-Objekt einer beliebigen Seite) in manchen Kontexten so verhlt, als wrde sie den Wert undefined nachbilden. Lose Gleichheit ist ein solcher Kontext: null == A und undefined == A ergeben genau dann true, wenn A ein Objekt ist, das undefined nachbildet. In allen anderen Fllen ist ein Objekt bei einem losen Vergleich niemals gleich undefined oder null.

In den meisten Fllen wird von der Verwendung loser Gleichheit abgeraten. Das Ergebnis eines strikten Gleichheitsvergleichs ist leichter vorherzusagen und kann wegen der fehlenden Typumwandlung mglicherweise schneller ermittelt werden.

Das folgende Beispiel zeigt lose Gleichheitsvergleiche zwischen dem primitiven Number-Wert 0, dem primitiven BigInt-Wert 0n, dem primitiven String-Wert '0' und einem Objekt, dessen toString()-Wert '0' ist.

js
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.

Wertgleichheit mit Object.is()

Wertgleichheit bestimmt, ob zwei Werte in allen Kontexten funktional identisch sind. (Dieser Anwendungsfall veranschaulicht das Liskovsche Substitutionsprinzip.) Ein Beispiel dafr ist der Versuch, eine unvernderliche Eigenschaft zu ndern:

js
// 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 lst beim Versuch, eine unvernderliche Eigenschaft zu ndern, eine Ausnahme aus. Wird jedoch keine tatschliche nderung verlangt, geschieht nichts. Wenn v den Wert -0 hat, wird keine nderung verlangt und kein Fehler ausgelst. Intern wird beim erneuten Definieren einer unvernderlichen Eigenschaft der neu angegebene Wert anhand der Wertgleichheit mit dem aktuellen Wert verglichen.

Wertgleichheit wird durch die Methode Object.is bereitgestellt. Sie wird in der Sprache fast berall dort verwendet, wo ein Wert mit identischer Wertidentitt erwartet wird.

SameValueZero-Gleichheit

Sie hnelt der Wertgleichheit, betrachtet aber +0 und -0 als gleich.

SameValueZero-Gleichheit wird nicht als JavaScript-API bereitgestellt, lsst sich aber mit eigenem Code implementieren:

js
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;
}

SameValueZero unterscheidet sich von strikter Gleichheit nur dadurch, dass NaN als gleich behandelt wird, und von Wertgleichheit nur dadurch, dass -0 als gleich 0 behandelt wird. Dadurch liefert es bei Suchvorgngen meist das sinnvollste Verhalten, insbesondere beim Umgang mit NaN. Es wird von Array.prototype.includes(), TypedArray.prototype.includes() sowie Methoden von Map und Set zum Vergleich von Schlsseln verwendet.

Vergleich der Gleichheitsmethoden

Das doppelte und das dreifache Gleichheitszeichen werden oft miteinander verglichen, indem eines als erweiterte Version des anderen bezeichnet wird. So knnte man das doppelte Gleichheitszeichen als Erweiterung des dreifachen betrachten, weil es alles tut, was auch das dreifache tut, aber zustzlich die Operanden umwandelt beispielsweise bei 6 == "6". Umgekehrt knnte man das doppelte Gleichheitszeichen als Ausgangspunkt und das dreifache als erweiterte Version betrachten, weil es verlangt, dass beide Operanden denselben Typ haben, und damit eine zustzliche Bedingung stellt.

Diese Sichtweise setzt jedoch voraus, dass Gleichheitsvergleiche ein eindimensionales Spektrum bilden, an dessen einem Ende vollstndig strikt und an dessen anderem Ende vollstndig lose steht. Bei Object.is greift dieses Modell zu kurz: Es ist weder loser als das doppelte Gleichheitszeichen noch strikter als das dreifache und liegt auch nicht irgendwo dazwischen (also zugleich strikter als das doppelte und loser als das dreifache Gleichheitszeichen). Die folgende Tabelle der Wertgleichheitsvergleiche zeigt, dass dies an der Behandlung von NaN durch Object.is liegt. Wrde Object.is(NaN, NaN) false ergeben, knnten wir es auf dem Lose-strikt-Spektrum als noch striktere Form des dreifachen Gleichheitszeichens einordnen, die zwischen -0 und +0 unterscheidet. Wegen der Behandlung von NaN trifft das jedoch nicht zu. Object.is muss daher anhand seiner konkreten Eigenschaften betrachtet werden und nicht danach, wie lose oder strikt es im Verhltnis zu den Gleichheitsoperatoren ist.

x y == === 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 Sie Object.is() statt des dreifachen Gleichheitszeichens verwenden sollten

Im Allgemeinen ist das besondere Verhalten von Object.is bei Nullen wohl nur bei bestimmten Metaprogrammierungstechniken von Interesse, insbesondere im Zusammenhang mit Eigenschaftsdeskriptoren, wenn Ihr Code einige Eigenschaften von Object.defineProperty nachbilden soll. Wenn Ihr Anwendungsfall dies nicht erfordert, sollten Sie Object.is vermeiden und stattdessen === verwenden. Selbst wenn Vergleiche zwischen zwei NaN-Werten bei Ihnen true ergeben sollen, ist es im Allgemeinen einfacher, NaN gesondert zu prfen (mit der bereits in frheren ECMAScript-Versionen verfgbaren Methode isNaN), als nachzuvollziehen, wie sich umgebende Berechnungen auf das Vorzeichen der Nullen auswirken knnten, die in Ihren Vergleichen auftreten.

Die folgende, nicht vollstndige Liste enthlt integrierte Methoden und Operatoren, durch die sich ein Unterschied zwischen -0 und +0 in Ihrem Code bemerkbar machen kann:

- (unre Negation)

Betrachten Sie das folgende Beispiel:

js
const stoppingForce = obj.mass * -obj.velocity;

Wenn obj.velocity den Wert 0 hat (oder 0 ergibt), entsteht an dieser Stelle ein -0, das an stoppingForce weitergegeben wird.

Math.atan2, Math.ceil, Math.pow, Math.round

In manchen Fllen knnen diese Methoden -0 als Rckgabewert in einen Ausdruck einbringen, selbst wenn keiner der Parameter -0 ist. Wird beispielsweise Math.pow verwendet, um -Infinity mit einem beliebigen negativen, ungeraden Exponenten zu potenzieren, ist das Ergebnis -0. Weitere Informationen finden Sie in der Dokumentation der einzelnen Methoden.

Math.floor, Math.max, Math.min, Math.sin, Math.sqrt, Math.tan

Diese Methoden knnen in manchen Fllen -0 zurckgeben, wenn einer der Parameter -0 ist. Beispielsweise ergibt Math.min(-0, +0) den Wert -0. Weitere Informationen finden Sie in der Dokumentation der einzelnen Methoden.

~, <<, >>

Jeder dieser Operatoren verwendet intern den ToInt32-Algorithmus. Da es im internen 32-Bit-Ganzzahltyp nur eine Darstellung fr 0 gibt, bleibt -0 bei einer Umwandlung und anschlieenden Rckumwandlung durch eine inverse Operation nicht erhalten. Beispielsweise ergeben sowohl Object.is(~~(-0), -0) als auch Object.is(-0 << 2 >> 2, -0) den Wert false.

Sich auf Object.is zu verlassen, ohne das Vorzeichen von Nullen zu bercksichtigen, kann problematisch sein. Wenn Sie ausdrcklich zwischen -0 und +0 unterscheiden mchten, liefert die Methode hingegen genau das gewnschte Ergebnis.

Einschrnkung: Object.is() und NaN

Object.is() behandelt alle Vorkommen von NaN als denselben Wert. Mit Typed Arrays knnen jedoch unterschiedliche Gleitkommadarstellungen von NaN vorliegen, die sich nicht in allen Kontexten identisch verhalten. Zum Beispiel:

js
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[7] |= 0x80;
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) [0, 0, 0, 0, 0, 0, 248, 255]

Hinweis: Implementierungen drfen die Bitdarstellung von NaN kanonisieren. Daher kann nan2 nach der Rckumwandlung in eine Gleitkommazahl dieselbe Bitdarstellung wie das ursprngliche NaN haben.

Siehe auch


Web Proxy Viewer  |  New URL  |  Original Page