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?"

87voto

Troy Harvey Punkte 2293

Wenn Sie arbeiten in AngularJS le angular.equals Funktion bestimmt, ob zwei Objekte gleich sind. Unter Ember.js verwenden. isEqual .

  • angular.equals - Siehe die docs o Quelle für weitere Informationen zu dieser Methode. Es macht auch einen tiefen Vergleich auf Arrays.
  • Ember.js isEqual - Siehe die docs o Quelle für weitere Informationen zu dieser Methode. Es wird kein tiefer Vergleich auf Arrays durchgeführt.

    var purple = [{"purple": "drank"}]; var drank = [{"purple": "drank"}];

    if(angular.equals(purple, drank)) { document.write('got dat'); }

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>

71voto

Joel Anair Punkte 13536

Wenn Sie eine JSON-Bibliothek verwenden, können Sie jedes Objekt als JSON kodieren und dann die resultierenden Zeichenketten auf Gleichheit vergleichen.

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

HINWEIS: Diese Lösung funktioniert zwar in vielen Fällen, ist aber, wie mehrere Personen in den Kommentaren anmerkten, aus einer Reihe von Gründen problematisch. In so gut wie allen Fällen sollten Sie eine robustere Lösung finden.

124 Stimmen

Interessant, aber meiner Meinung nach etwas heikel. Können Sie zum Beispiel zu 100 % garantieren, dass die Objekteigenschaften immer in der gleichen Reihenfolge generiert werden?

33 Stimmen

Das ist eine gute Frage, die eine weitere aufwirft, nämlich die, ob zwei Objekte mit denselben Eigenschaften in unterschiedlicher Reihenfolge wirklich gleich sind oder nicht. Das hängt davon ab, was man unter "gleich" versteht, denke ich.

0 Stimmen

Dies ist definitiv eine der interessanteren Antworten und eine gute Information - danke für die Weitergabe!

41voto

Vaelin Punkte 526

Für diejenigen unter Ihnen, die Node verwenden, gibt es eine praktische Methode namens isDeepStrictEqual auf den einheimischen util Bibliothek, die dies leisten kann.

const util = require('util');

const obj1 = {
  foo: "bar",
  baz: [1, 2]
};

const obj2 = {
  foo: "bar",
  baz: [1, 2]
};

obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true

https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2

0 Stimmen

Seine Leistung soll gut sein. Keine Sorge. Auch ich verwende es in komplexen Szenarien. Als wenn wir dies verwenden, müssen wir nicht zu kümmern, wenn Eigenschaft des Objekts ist mit Objekt oder Array. Json.Stringify macht es sowieso String und Vergleich von Strings in Javascript ist nicht eine große Sache

0 Stimmen

Ich danke Ihnen für diese Antwort! Es ist in Node v9 hinzugefügt

30voto

Rafael Xavier Punkte 2740

In Node.js können Sie die native require("assert").deepStrictEqual . Mehr Informationen: http://nodejs.org/api/assert.html

Zum Beispiel:

var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

Ein weiteres Beispiel, das folgende Ergebnisse liefert true / false anstatt Fehler zurückzugeben:

var assert = require("assert");

function deepEqual(a, b) {
    try {
      assert.deepEqual(a, b);
    } catch (error) {
      if (error.name === "AssertionError") {
        return false;
      }
      throw error;
    }
    return true;
};

0 Stimmen

Chai verfügt ebenfalls über diese Funktion. In seinem Fall werden Sie verwenden: var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;

0 Stimmen

Einige Versionen von Node.js setzen error.name a "AssertionError [ERR_ASSERTION]" . In diesem Fall würde ich die if-Anweisung ersetzen durch if (error.code === 'ERR_ASSERTION') { .

0 Stimmen

Ich hatte keine Ahnung deepStrictEqual war der richtige Weg. Ich habe mir das Hirn zermartert, um herauszufinden, warum strictEqual hat nicht funktioniert. Fantastisch.

28voto

Aidin Punkte 14389

Auf diese Frage gibt es bereits mehr als 30 Antworten. Ich werde sie zusammenfassen und erklären (mit einer Analogie zu "meinem Vater") und meinen Lösungsvorschlag hinzufügen.

Sie haben 4+1 Klassen von Lösungen :


1) Verwenden Sie einen hakeligen, unvollständigen, schnellen Einzeiler

Gut, wenn man es eilig hat und 99 % Korrektheit funktioniert.

Beispiele hierfür sind, JSON.stringify() vorgeschlagen von Pratik Bhalodiya , oder JSON.encode von Joel Anair , oder .toString() oder andere Methoden, die Ihre Objekte in einen String umwandeln und dann die beiden Strings mit === Zeichen für Zeichen.

Der Nachteil ist jedoch, dass es keine weltweit einheitliche Darstellung eines Objekts in String gibt. z.B. { a: 5, b: 8} y {b: 8 and a: 5 } gleich sind.

  • Vorteile: Schnell, schnell.
  • Nachteile: Hoffentlich funktioniert! Es wird nicht funktionieren, wenn die Umgebung/Browser/Engine die Reihenfolge der Objekte speichert (z.B. Chrome/V8) und die Reihenfolge der Schlüssel unterschiedlich ist (Dank an Eksapsy .) Also, überhaupt nicht garantiert. Die Leistung wäre auch bei großen Objekten nicht besonders gut.

Mein Vater Analogie

Wenn ich über meinen Vater spreche, " mein großer, gut aussehender Vater " und " mein gut aussehender großer Vater " sind ein und dieselbe Person! Aber die beiden Strings sind nicht identisch.

Beachten Sie, dass es tatsächlich eine korrekte (Standardweg) Bestellung der Adjektive in der englischen Grammatik, die sagt es sollte ein "gutaussehender großer Mann" sein, aber Sie riskieren Ihre Kompetenz, wenn Sie blind davon ausgehen, dass die Javascript-Engine von iOS 8 Safari auch die gleiche Grammatik befolgt, blind! #WelcomeToJavascriptNonStandards


2) Schreibe deine eigene rekursive Funktion

Gut, wenn Sie noch lernen.

Beispiele sind atmin's Lösung .

Der größte Nachteil besteht darin, dass man definitiv einige Randfälle übersehen wird. Haben Sie eine Selbstreferenz in Objektwerten? Haben Sie bedacht NaN ? Haben Sie zwei Objekte betrachtet, die dieselbe ownProperties aber verschiedene prototypische Eltern?

Ich würde die Leute nur dazu ermutigen, dies zu tun, wenn sie üben und der Code nicht in die Produktion gehen soll. Das ist der einzige Fall, der das Rad neu erfinden hat seine Berechtigung.

  • Vorteile: Gelegenheit zum Lernen.
  • Nachteile: Nicht zuverlässig. Zeitaufwendig und bedenklich.

Mein Vater Analogie

Wenn mein Vater "John Smith" heißt und am "1.1.1970" Geburtstag hat, ist jeder, der "John Smith" heißt und am "1.1.1970" geboren ist, mein Vater.

Das ist normalerweise der Fall, aber was ist, wenn es zwei "John Smiths" gibt, die an diesem Tag geboren wurden? Wenn Sie die Größe der beiden berücksichtigen, erhöht das zwar die Genauigkeit, ist aber immer noch kein perfekter Vergleich.

2.1 Ihr begrenzter Umfang DIY-Vergleicher

Anstatt alle Eigenschaften rekursiv zu prüfen, könnte man in Erwägung ziehen, nur eine "begrenzte" Anzahl von Eigenschaften zu prüfen. Zum Beispiel, wenn die Objekte User s, können Sie deren emailAddress Feld.

Sie ist zwar immer noch nicht perfekt, aber die Vorteile gegenüber Lösung Nr. 2 liegen auf der Hand:

  1. Es ist vorhersehbar, und die Wahrscheinlichkeit eines Absturzes ist geringer.
  2. Sie steuern die "Definition" der Gleichheit, anstatt sich auf eine wilde Form und Gestalt des Objekts und dessen Prototyp und verschachtelte Eigenschaften zu verlassen.

3) Verwenden Sie eine Bibliotheksversion von equal 機能

Gut, wenn Sie eine Qualität auf Produktionsniveau benötigen und Sie das Design des Systems nicht ändern können.

Beispiele sind _.equal von lodash , bereits in coolaj86's Antwort oder die von Angular oder Ember, wie sie in Tony Harveys Antwort oder Node's von Rafael Xavier .

  • Vorteile: Das tun alle anderen auch.
  • Nachteile: Externe Abhängigkeit, die zusätzliche Kosten für Arbeitsspeicher, CPU und Sicherheit verursachen kann, wenn auch nur in geringem Maße. Außerdem können immer noch einige Randfälle übersehen werden (z.B. ob zwei Objekte mit demselben ownProperties aber verschiedene prototypische Eltern sollten als gleich angesehen werden oder nicht). Zum Schluss, vous könnte ungewollt ein zugrundeliegendes Design-Problem damit beheben; ich sage es nur!

Mein Vater Analogie

Das ist so, als würde ich eine Agentur dafür bezahlen, meinen biologischen Vater anhand seiner Telefonnummer, seines Namens, seiner Adresse usw. ausfindig zu machen.

Es wird mehr kosten, und es ist wahrscheinlich genauer, als wenn ich den Background Check selbst durchführe, aber es deckt keine Sonderfälle ab, z.B. wenn mein Vater Einwanderer/Asylant ist und sein Geburtstag unbekannt ist!


4) Verwenden Sie einen IDentifier im Objekt

Gut, wenn Sie das Design des Systems (Objekte, mit denen Sie arbeiten) [noch] ändern können und Sie wollen, dass Ihr Code lange hält.

Sie ist nicht in allen Fällen anwendbar und möglicherweise nicht sehr leistungsfähig. Es ist jedoch eine sehr zuverlässige Lösung, wenn man es schaffen kann.

Die Lösung ist, dass jeder object im System haben einen einzigartig Identifikator zusammen mit allen anderen Eigenschaften. Die Einzigartigkeit des Identifikators wird zum Zeitpunkt der Generierung garantiert. Und Sie werden diese ID (auch bekannt als UUID/GUID -- Global/universell eindeutiger Identifikator ), wenn es um den Vergleich zweier Objekte geht, d.h. sie sind gleich, wenn diese IDs gleich sind.

Die IDs können einfach sein auto_incremental Zahlen, oder eine Zeichenkette, die über eine Bibliothek (empfohlen) oder ein Stück Code . Alles, was Sie tun müssen, ist sicherzustellen, dass es immer eindeutig ist, was im Falle von auto_incremental kann eingebaut werden, oder im Fall von UUID können alle vorhandenen Werte geprüft werden (z.B. MySQL's UNIQUE Spaltenattribut) oder einfach (wenn sie aus einer Bibliothek stammen) auf die extrem niedrige Wahrscheinlichkeit einer Kollision verlassen werden.

Beachten Sie, dass Sie die ID immer zusammen mit dem Objekt speichern müssen (um ihre Einzigartigkeit zu gewährleisten), und dass die Berechnung in Echtzeit möglicherweise nicht der beste Ansatz ist.

  • Vorteile: Zuverlässig, effizient, nicht schmutzig, modern.
  • Nachteile: Braucht zusätzlichen Platz. Möglicherweise muss das System umgestaltet werden.

Mein Vater Analogie

Es ist, als wüsste man, dass die Sozialversicherungsnummer meines Vaters 911-345-9283 lautet, so dass jeder, der diese SSN hat, mein Vater ist, und jeder, der behauptet, mein Vater zu sein, diese SSN haben muss.


Schlussfolgerung

Ich persönlich bevorzuge die Lösung Nr. 4 (ID), weil sie genauer und zuverlässiger ist als alle anderen. Wenn das nicht möglich ist, würde ich für die Vorhersagbarkeit die Lösung 2.1 und dann die Lösung 3 wählen. Wenn beides nicht möglich ist, dann #2 und schließlich #1.

2 Stimmen

Die erste "hacky" Lösung funktioniert auch nicht, wenn die Reihenfolge der Objekte unterschiedlich ist. z.B.. o1 = { a: '1', b: '2' } - o2 = { b: '2', a: '1' } compare JSON.stringify(o1) === JSON.stringify(o2) = false

1 Stimmen

Ihre bevorzugte Methode geht davon aus, dass das Objekt eine bestimmte Verwendung hat, z. B. einen Datensatz für ein eindeutiges Objekt, im Gegensatz zu einer anderen Verwendung, z. B. einer Menge von Elementen mit Wiederholung, bei der der Schlüssel/die Eigenschaft das Element ist und der Wert die Anzahl der Elemente in der Menge ist (was erfordern würde, dass jede Eigenschaft und jeder Wert überprüft wird).

0 Stimmen

@DaveF in diesem Fall, vielleicht Karte wäre semantisch besser geeignet als ein wildes Objekt.

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