💯 Testen von Equality auf Klasseninstanzen - Javascript
Ein gängiges Muster in der objektorientierten Programmierung ist die Definition einer benutzerdefinierten equals
-Methode innerhalb der Klasse. Du kannst sie implementieren, um Instanzen zu vergleichen, indem du ihre internen Werte prüfst:
class Container {
#name;
constructor(a) {
this.#name = a;
}
valueOf() {
return this.#name;
}
equals(other) {
return other instanceof Container && this.valueOf() === other.valueOf();
}
}
main();
function main() {
const a = new Container("XYZ");
const b = new Container("XYZ");
test(1, a < b, false); // 1. BESTANDEN
test(2, a <= b, true); // 2. BESTANDEN
test(3, a === b, true); // 3. FEHLER (a und b sind unterschiedliche Instanzen)
test(4, a.equals(b), true); // 4. PASS (mit der benutzerdefinierten Methode „equals“)
test(5, a.valueOf() === b.valueOf(), true); // 5. PASS
}
function test(n, a, x) {
if (a === x) console.log(n + ". PASS");
else console.log(n + ". FAIL: expected " + x);
}
Auf diese Weise kannst du zwei Instanzen mit „a.equals(b)“ vergleichen, um anhand der internen Werte auf Gleichheit zu prüfen.
Wenn du in JavaScript zwei Klasseninstanzen mit „===“ vergleichst, wird auf Referenzgleichheit geprüft, d. h. es wird verglichen, ob beide Variablen auf genau dasselbe Objekt im Speicher verweisen. Aus diesem Grund schlägt „a === b“ in Test 3 fehl: „a“ und „b“ sind zwei verschiedene Instanzen der Klasse „Container“, auch wenn sie möglicherweise denselben internen Wert („XYZ“) enthalten. Die Methode valueOf
wird bei solchen Vergleichen nicht automatisch aufgerufen.
Für Test 4
ruft der Ausdruck +a === +b
valueOf()
auf, da der unäre +
-Operator versucht, das Objekt in eine primitive Zahl umzuwandeln (mit valueOf
). Da valueOf
jedoch eine Zeichenfolge ("XYZ"
) zurückgibt, versucht JavaScript, diese Zeichenfolge in eine Zahl umzuwandeln. Da "XYZ"
nicht in eine Zahl umgewandelt werden kann, ergibt sich NaN
. Daher gibt der Vergleich NaN === NaN
false
zurück, was zu einem fehlgeschlagenen Test führt.
Weitere Möglichkeiten zum Vergleichen der Werte innerhalb der Klasseninstanzen:
Verlassen Sie sich bei Vergleichen explizit auf valueOf
Du kannst bei Vergleichen immer die Methode .valueOf()
verwenden, um den internen Wert explizit zu extrahieren:
test(4, a.valueOf() === b.valueOf(), true); // Dies funktioniert, da Sie die Zeichenfolgenwerte direkt vergleichen.
Dieser Ansatz funktioniert, ist aber weniger elegant als die Definition einer equals
-Methode, weil du immer daran denken musst, valueOf()
aufzurufen.
Bedenke:
- Test 3 (
a === b
) schlägt fehl, da===
auf Referenzgleichheit prüft, nicht auf Wertegleichheit. - Test 4 (
+a === +b
) schlägt fehl, da+a
versucht, die Zeichenfolge"XYZ"
in eine Zahl umzuwandeln, was zuNaN
führt. - Die einfachste Lösung besteht darin, Ihrer Klasse eine
equals
-Methode zum Wertevergleich hinzuzufügen oder explizita.valueOf() === b.valueOf()
für Gleichheitsprüfungen zu verwenden.
Bonus: Verbesserung der Lesbarkeit durch Verwendung von Symbol.toPrimitive
Du kannst Symbol.toPrimitive
auch definieren, um zu steuern, wie das Objekt bei Bedarf in ein Primitiv (Zeichenfolge oder Zahl) umgewandelt wird:
class Container {
#name;
constructor(a) {
this.#name = a;
}
valueOf() {
return this.#name;
}
[Symbol.toPrimitive](hint) {
if (hint === "string") {
return this.#name; // Bei Umwandlung in Zeichenfolge
}
return this.#name; // Umwandlung in Standard (Zahl oder Zeichenfolge)
}
}
main();
function main() {
const a = new Container("XYZ");
const b = new Container("XYZ");
test(1, a < b, false); // 1. PASS
test(2, a <= b, true); // 2. BESTANDEN
test(3, a === b, true); // 3. FEHLER (a und b beginnen immer noch unterschiedliche Instanzen)
test(4, a + '' === b + '', true); // 4. BESTANDEN (String-Koerzierung funktioniert dank Symbol.toPrimitive)
test(5, a.valueOf() === b.valueOf(), true); // 5. BESTANDEN
}
Dadurch kannst Du steuern, wie sich das Objekt verhält, wenn es in eine Primitive umgewandelt wird (wie mit +a
oder a + ""
für die String-Verkettung).