Hier ist mein ES3 kommentierte Lösung (blutige Details nach dem Code):
function object_equals( x, y ) {
if ( x === y ) return true;
// if both x and y are null or undefined and exactly the same
if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;
// if they are not strictly equal, they both need to be Objects
if ( x.constructor !== y.constructor ) return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
for ( var p in x ) {
if ( ! x.hasOwnProperty( p ) ) continue;
// other properties were tested using x.constructor === y.constructor
if ( ! y.hasOwnProperty( p ) ) return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if ( x[ p ] === y[ p ] ) continue;
// if they have the same strict value or identity then they are equal
if ( typeof( x[ p ] ) !== "object" ) return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if ( ! object_equals( x[ p ], y[ p ] ) ) return false;
// Objects and Arrays must be tested recursively
}
for ( p in y )
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) )
return false;
// allows x[ p ] to be set to undefined
return true;
}
Bei der Entwicklung dieser Lösung habe ich besonders auf Eckfälle und Effizienz geachtet und versucht, eine einfache Lösung zu finden, die hoffentlich mit einer gewissen Eleganz funktioniert. JavaScript erlaubt sowohl null y undefiniert Eigenschaften und Objekte haben Prototypenketten die zu sehr unterschiedlichen Verhaltensweisen führen können, wenn sie nicht kontrolliert werden.
Erstens habe ich mich dafür entschieden, nicht zu verlängern Objekt.Prototyp vor allem, weil null nicht eines der Objekte des Vergleichs sein kann und dass ich glaube, dass null sollte ein gültiges Objekt zum Vergleich mit einem anderen sein. Es gibt auch andere berechtigte Bedenken, die von anderen hinsichtlich der Ausweitung der Objekt.Prototyp über mögliche Nebenwirkungen auf den Code anderer.
Besondere Aufmerksamkeit muss der Möglichkeit gewidmet werden, dass JavaScript erlaubt, Objekteigenschaften auf undefiniert , d.h. es gibt Eigenschaften, deren Werte auf undefiniert . Die obige Lösung stellt sicher, dass beide Objekte die gleichen Eigenschaften haben, die auf undefiniert Gleichheit zu melden. Dies kann nur erreicht werden, indem das Vorhandensein von Eigenschaften mit Object.hasOwnProperty( property_name ) . Beachten Sie auch, dass JSON.stringify() entfernt die Eigenschaften, die auf undefiniert und dass daher Vergleiche, die dieses Formular verwenden, Eigenschaften ignorieren, die auf den Wert undefiniert .
Funktionen sollten nur dann als gleichwertig betrachtet werden, wenn sie denselben Verweis haben, nicht nur denselben Code, da dies den Prototyp dieser Funktionen nicht berücksichtigen würde. Der Vergleich der Code-Zeichenkette garantiert also nicht, dass sie das gleiche Prototyp-Objekt haben.
Die beiden Objekte sollten die gleiche Prototypenkette und nicht nur die gleichen Eigenschaften. Dies kann nur browserübergreifend getestet werden, indem man die Konstrukteur der beiden Objekte für strikte Gleichheit. ECMAScript 5 würde es ermöglichen, ihren aktuellen Prototyp zu testen, indem man Object.getPrototypeOf() . Einige Webbrowser bieten auch eine __proto__ Eigenschaft, die das Gleiche bewirkt. Eine mögliche Verbesserung des obigen Codes würde es ermöglichen, eine dieser Methoden zu verwenden, wann immer sie verfügbar ist.
Die Verwendung strenger Vergleiche ist hier von entscheidender Bedeutung, denn 2 sollte nicht gleichgesetzt werden mit "2.0000" noch falsch sollte gleichgesetzt werden mit null , undefiniert , oder 0 .
Aus Gründen der Effizienz möchte ich so schnell wie möglich die Gleichheit der Eigenschaften vergleichen. Erst wenn dies nicht gelingt, suche ich nach der typeof diese Eigenschaften. Der Geschwindigkeitszuwachs könnte bei großen Objekten mit vielen skalaren Eigenschaften erheblich sein.
Es sind nicht mehr als zwei Schleifen erforderlich, die erste, um die Eigenschaften des linken Objekts zu prüfen, die zweite, um die Eigenschaften des rechten Objekts zu prüfen und nur das Vorhandensein (nicht den Wert) zu überprüfen, um diese Eigenschaften zu erfassen, die mit der Option undefiniert Wert.
Insgesamt behandelt dieser Code die meisten Eckfälle in nur 16 Zeilen Code (ohne Kommentare).
Update (13.8.2015) . Ich habe eine bessere Version implementiert, da die Funktion wert_equals() das schneller ist, Eckfälle wie NaN und 0, die sich von -0 unterscheiden, richtig behandelt, optional die Reihenfolge der Eigenschaften von Objekten erzwingt und auf zyklische Referenzen prüft, unterstützt durch mehr als 100 automatisierte Tests als Teil der Toubkal Projekt-Testsuite.