995 Stimmen

Wie bestimmt man die Gleichheit zweier JavaScript-Objekte?

Ein strikter Gleichheitsoperator sagt Ihnen, ob zwei Objekte Typen gleich sind. Gibt es jedoch eine Möglichkeit zu erkennen, ob zwei Objekte gleich sind, ähnlich wie der Hash-Code Wert in Java?

Stack Overflow Frage Gibt es irgendeine Art von HashCode-Funktion in JavaScript? ist dieser Frage ähnlich, erfordert aber eine eher akademische Antwort. Das obige Szenario zeigt, warum es notwendig wäre, eine zu haben, und ich frage mich, ob es irgendeine gleichwertige Lösung .

5 Stimmen

Gehen Sie auch dieser Frage nach stackoverflow.com/q/1068834/1671639

55 Stimmen

Beachten Sie, dass auch in Java, a.hashCode() == b.hashCode() tut no bedeuten, dass a ist gleich b . Das ist eine notwendige, aber keine hinreichende Bedingung.

7 Stimmen

Wenn Sie Objekte in Ihrem Code vergleichen MÜSSEN, dann haben Sie Ihren Code wahrscheinlich falsch geschrieben. Die bessere Frage könnte lauten: "Wie kann ich diesen Code so schreiben, dass ich keine Objekte vergleichen muss?"

710voto

coolaj86 Punkte 69032

Warum das Rad neu erfinden? Geben Sie Lodash einen Versuch. Es bietet eine Reihe von unverzichtbaren Funktionen wie isEqual() .

_.isEqual(object, other);

Es wird jeden Schlüsselwert - genau wie bei den anderen Beispielen auf dieser Seite - mit Brute-Force-Methoden überprüfen. ECMAScript 5 und native Optimierungen, wenn sie im Browser verfügbar sind.

Hinweis: Zuvor empfahl diese Antwort Underscore.js mais lodash hat bei der Behebung von Fehlern und der konsequenten Behandlung von Problemen bessere Arbeit geleistet.

62 Stimmen

Underscore's isEqual Funktion ist sehr schön (aber Sie haben in ihrer Bibliothek zu ziehen, um es zu benutzen - etwa 3K gzipped).

15 Stimmen

Selbst wenn Sie es sich nicht leisten können, underscore als Abhängigkeit zu haben, ziehen Sie die isEqual-Funktion heraus, erfüllen Sie die Lizenzanforderungen und machen Sie weiter. Es ist bei weitem der umfassendste Gleichheitstest, der auf Stackoverflow erwähnt wird.

0 Stimmen

@DaleAnderson Haha, und jetzt bin ich wieder hier und schaue mir das Gleiche an, nicht weil ich Underscore nicht benutzen kann, sondern weil ich es eigentlich nicht will, und in der Tat wollte ich mir etwas wie seine isEqual für "Inspiration".

226voto

AnthonyWJones Punkte 182582

Die kurze Antwort

Die Antwort ist einfach: Nein, es gibt kein allgemeines Mittel, um zu bestimmen, dass ein Objekt einem anderen in dem von Ihnen gemeinten Sinne gleich ist. Die Ausnahme ist, wenn Sie strikt davon ausgehen, dass ein Objekt typfrei ist.

Die lange Antwort

Das Konzept ist das einer Equals-Methode, die zwei verschiedene Instanzen eines Objekts vergleicht, um anzuzeigen, ob sie auf einer Werteebene gleich sind. Es ist jedoch Sache des jeweiligen Typs, zu definieren, wie eine Equals Methode implementiert werden sollte. Ein iterativer Vergleich von Attributen, die primitive Werte haben, reicht möglicherweise nicht aus: Ein Objekt kann Attribute enthalten, die für die Gleichheit nicht relevant sind. Zum Beispiel,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

In diesem Fall, c ist es nicht wirklich wichtig, festzustellen, ob zwei Instanzen von MyClass gleich sind, nur a y b sind wichtig. In einigen Fällen c kann von Fall zu Fall variieren und beim Vergleich nicht signifikant sein.

Beachten Sie, dass dieses Problem auftritt, wenn Mitglieder selbst auch Instanzen eines Typs sein können und diese alle ein Mittel zur Bestimmung der Gleichheit haben müssen.

Erschwerend kommt hinzu, dass in JavaScript die Unterscheidung zwischen Daten und Methoden unscharf ist.

Ein Objekt kann auf eine Methode verweisen, die als Ereignisbehandlungsroutine aufgerufen werden soll, und dies würde wahrscheinlich nicht als Teil seines "Wertstatus" betrachtet werden. Einem anderen Objekt hingegen kann sehr wohl eine Funktion zugewiesen werden, die eine wichtige Berechnung durchführt und damit diese Instanz von anderen unterscheidet, nur weil sie auf eine andere Funktion verweist.

Was ist mit einem Objekt, bei dem eine seiner vorhandenen Prototypmethoden von einer anderen Funktion überschrieben wird? Kann es trotzdem als gleichwertig mit einer anderen Instanz betrachtet werden, die ansonsten identisch ist? Diese Frage kann nur im Einzelfall für jeden Typ beantwortet werden.

Wie bereits erwähnt, wäre die Ausnahme ein streng typloses Objekt. In diesem Fall ist die einzige sinnvolle Wahl ein iterativer und rekursiver Vergleich der einzelnen Elemente. Selbst dann muss man sich fragen, was der "Wert" einer Funktion ist.

209 Stimmen

Wenn Sie einen Unterstrich verwenden, können Sie einfach _.isEqual(obj1, obj2);

16 Stimmen

@Harsh, die Antwort hat keine Lösung gebracht, weil es keine gibt. Auch in Java gibt es keinen Königsweg zum Vergleich der Objektgleichheit und zur korrekten Implementierung der .equals Methode ist nicht trivial, deshalb gibt es ein eigenes Thema in Leistungsfähiges Java .

4 Stimmen

@Kumar Harsh, Was zwei Objekte gleich macht, ist sehr anwendungsspezifisch; nicht jede Eigenschaft eines Objekts sollte notwendigerweise berücksichtigt werden, daher ist das Brute-Forcing jeder Eigenschaft eines Objekts auch keine konkrete Lösung.

202voto

Daniel X Moore Punkte 13868

Der Standard-Gleichheitsoperator in JavaScript für Objekte ergibt true, wenn sie sich auf dieselbe Stelle im Speicher beziehen.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

Wenn Sie einen anderen Gleichheitsoperator benötigen, müssen Sie eine equals(other) Methode oder etwas Ähnliches zu Ihren Klassen hinzufügen, und die Besonderheiten Ihres Problembereichs werden bestimmen, was genau das bedeutet.

Hier ist ein Beispiel für eine Spielkarte:

function Card(rank, suit) {
  this.rank = rank;
  this.suit = suit;
  this.equals = function(other) {
     return other.rank == this.rank && other.suit == this.suit;
  };
}

var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");

queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true

0 Stimmen

Wenn das/die Objekt(e) in eine JSON-Zeichenkette umgewandelt werden kann/können, ist eine equals()-Funktion einfach.

6 Stimmen

@scotts Nicht immer. Die Konvertierung von Objekten in JSON und der Vergleich von Zeichenketten kann bei komplexen Objekten in engen Schleifen sehr rechenintensiv werden. Bei einfachen Objekten spielt das wahrscheinlich keine große Rolle, aber in Wirklichkeit hängt es von Ihrer spezifischen Situation ab. Eine korrekte Lösung kann so einfach sein wie der Vergleich von Objekt-IDs oder die Überprüfung jeder einzelnen Eigenschaft, aber ihre Korrektheit hängt ganz von der Problemdomäne ab.

1 Stimmen

Sollten wir nicht auch den Datentyp vergleichen?! return other.rank === this.rank && other.suit === this.suit;

114voto

atmin Punkte 811

Kurz funktionell deepEqual Umsetzung:

function deepEqual(x, y) {
  return (x && y && typeof x === 'object' && typeof y === 'object') ?
    (Object.keys(x).length === Object.keys(y).length) &&
      Object.keys(x).reduce(function(isEqual, key) {
        return isEqual && deepEqual(x[key], y[key]);
      }, true) : (x === y);
}

編集 Version 2, unter Verwendung des Vorschlags von jib und der ES6-Pfeilfunktionen:

function deepEqual(x, y) {
  const ok = Object.keys, tx = typeof x, ty = typeof y;
  return x && y && tx === 'object' && tx === ty ? (
    ok(x).length === ok(y).length &&
      ok(x).every(key => deepEqual(x[key], y[key]))
  ) : (x === y);
}

14 Stimmen

Sie könnten ersetzen reduce mit every zu vereinfachen.

0 Stimmen

@jib Nein, das konnte er nicht.

3 Stimmen

@nonkertompf Sicher könnte er das: Object.keys(x).every(key => deepEqual(x[key], y[key])) .

88voto

Ebrahim Byagowi Punkte 8964

Dies ist meine Version. Sie verwendet neue Objekt.Schlüssel Funktion, die in ES5 eingeführt wird, und Ideen/Tests aus + , + y + :

function objectEquals(x, y) {
    'use strict';

    if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
    // after this just checking type of one would be enough
    if (x.constructor !== y.constructor) { return false; }
    // if they are functions, they should exactly refer to same one (because of closures)
    if (x instanceof Function) { return x === y; }
    // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
    if (x instanceof RegExp) { return x === y; }
    if (x === y || x.valueOf() === y.valueOf()) { return true; }
    if (Array.isArray(x) && x.length !== y.length) { return false; }

    // if they are dates, they must had equal valueOf
    if (x instanceof Date) { return false; }

    // if they are strictly equal, they both need to be object at least
    if (!(x instanceof Object)) { return false; }
    if (!(y instanceof Object)) { return false; }

    // recursive object equality check
    var p = Object.keys(x);
    return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
        p.every(function (i) { return objectEquals(x[i], y[i]); });
}

///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
    if (x) { document.write('<div style="color: green;">Passed</div>'); }
    else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));

assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));

assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));

assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));

assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
    assertTrue = assert.isTrue;

assertFalse({}.equals(null));
assertFalse({}.equals(undefined));

assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));

assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));

assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));

assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var j = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};

assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));

// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));

// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));

0 Stimmen

objectEquals([1,2,undefined],[1,2]) gibt zurück. true

0 Stimmen

objectEquals([1,2,3],{0:1,1:2,2:3}) gibt auch zurück true -- z.B. gibt es keine Typüberprüfung, sondern nur eine Schlüssel/Wert-Überprüfung.

0 Stimmen

objectEquals(new Date(1234),1234) gibt zurück. true

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X