Nachdem ich letzte und vorletzte Woche auf Javascript und this
eingegangen bin, möchte ich diese Woche darauf eingehen wie wir in Javascript Objekte vergleichen können… Oder eher: Warum das gar nicht so einfach ist!
Vor kurzem bin ich über eine interessanten Blog-Eintrag auf designpepper.com gestoßen zum Thema „Objektgleichheit in JavaScript“. Von dort habe ich mir übrigens auch die Code-Snippets geliehen 😉
Vergleichsoperatoren sind nicht genug
Bevor jemand auf die Idee kommt mit ==
oder ===
zu arbeiten: Das wird nicht ausreichen:
// Simple equality.
var jangoFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var bobaFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
console.log(bobaFett === jangoFett);
// Returns false
console.log(bobaFett == jangoFett);
// Still returns false
Die Ursache, wieso diese Vergleiche fehlschlagen, liegt daran das Javascript verschiedene Typen unterschiedlich miteinander vergleicht. Bei primitiven Typen wie Strings und Nummern werden die Inhalte miteinander verglichen – Andere Typen wie Arrays, einfache- und Date-Objekte werden anhand ihrer Referenz verglichen… Da die beiden Objekte unterschiedliche Referenzen beherbergen schlägt der Vergleich fehl!
Hier ein weiteres Beispiel von designpepper.com:
var jangoFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var bobaFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var callMeJango = jangoFett;
console.log(bobaFett === jangoFett);
// Returns false
console.log(callMeJango === jangoFett);
// Returns true
Hier verwenden callMeJango
und jangoFett
auf die gleiche Referenz – daher sind die Objekte (für Javascript) identisch.
Eine Funktion muss her!
Falls euch die Javascript-interne Prüfung nicht genügt und ihr lieber Inhalte miteinander vergleicht kommt ihr an einer eigenen Funktion nicht vorbei. Diese könnte folgendermaßen aussehen (erneut von designpepper.com):
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
console.log(isEquivalent(bobaFett, jangoFett));
// Returns true!
Diese Funktion ist relativ einfach aufgebaut, ich möchte von oben nach unten durchgehen:
- Zuerst werden die Anzahl der Attribute miteinander verglichen. Stimmen diese nicht überein, sind die Objekte definitiv unterschiedlich
- Als nächstes werden die Attribute durchlaufen und auf den gleichen Inhalt geprüft - Unterscheidet sich z.B.
jangoFett['occupation']
vonbobaFett['occupation']
wird sofort abgebrochen! - Sollte keine der Vergleiche fehlschlagen handelt es sich um ein identisches Objekt!
Aber Achtung! Diese Funktion wird zwar mit einfachen Objekte funktionieren, doch komplexe(re) Objekte werden nicht korrekt überprüft werden... Stellt euch folgende Situationen vor:
- Was, wenn eines der Attribute ein Objekt beinhaltet?
- Was, wenn eines der Attribute
NaN
beinhaltet? Dieser Wert wird von Javascript nie als gleich angesehen (NaN === NaN
ergibtfalse
!) - Was, wenn Objekt
a
ein Attribut mit dem Wertundefined
hat, währendb
dieses Attribut nicht hat (was intern mitundefined
übereinstimmt) ?
Was kann ich also tun?
Die erste Möglichkeit wäre die Funktion von oben einfach zu erweitern... Je nachdem, was für weitere Abfragen ihr benötigt!
Die zweite Möglichkeit wäre eine Bibliothek zu benutzen. Eine relativ gute Funktion zum checken auf Gleichheit gibt es in Underscore.js, diese geht über knapp 90 Zeilen und kann sich bei bedarf selbst aufrufen (Rekursive Funktion). Lo-Dash bietet eine ähnliche Funktion an - diese geht allerdings über 160 Zeilen und arbeitet ebenfalls rekursiv.
Habt ihr vielleicht noch weitere Tipps um "gleiche" Objekte miteinander zu vergleichen oder kennt einige Stolperfallen? Schreibt es in die Kommentare! Damit verabschiede ich mich für diese Woche und wünsche euch eine angenehme und sonnige Rest-Woche!
Bis dahin wünsche ich euch Happy Coding!
Hi,
erst mal klasse Lösung!
Wäre es nicht einfacher wenn man die Überprüfung so machen würde?
console.log(bobaFett.toString() === jangoFett.toString());
Gruß Christoph
Hallo ChristophJP,
vielen Dank für dein Kommentar – auf die „toString()“ Methode bin ich hier tatsächlich gar nicht eingegangen. Führt man „bobaFett.toString()“ aus, erhält man als Ergebnis den String „[object Object]“ – das gleiche Resultat bekommt man aber bei allen anderen Objekten auch, wie z.B. bei:
In Javascript gibt es leider keine einfache Funktionen, wie man sie aus PHP kennt (wie etwa get_class).
Viele Grüße
Leo
Hi Leo,
Ich verwende die .toString() Methode für das vergleichen von Date Objekten und hatte nicht in Erwägung gezogen das, das natürlich bei Normalen Objekten nicht funktionieren kann.
Gruß Christoph
Hi,
man kann evtl. noch mit JSON arbeiten. JSON.stringify(obj) … und dann die Strings vergleichen.
Grüße
Hallo Daniel,
das ist richtig, die Funktion
JSON.stringify(<obj>);
dürfte in den meisten Fälle ausreichen! Wenn man allerdings Funktionen oder Referenzen innerhalb der Objekte hat werden diese herausgeworfen bzw. in den statischen String übernommen.Viele Grüße
Leo