Вопрос

Оператор строгого равенства сообщит вам, если два объекта типы равны.Однако есть ли способ определить, равны ли два объекта? очень похоже на хеш-код значение в Java?

Вопрос о переполнении стека Есть ли в JavaScript какая-либо функция hashCode? похож на этот вопрос, но требует более академического ответа.Приведенный выше сценарий демонстрирует, почему это необходимо, и мне интересно, есть ли какие-либо эквивалентное решение.

Это было полезно?

Решение

Короткий ответ

Простой ответ:Нет, не существует общего способа определить, что один объект равен другому в том смысле, который вы имеете в виду.Исключение составляют случаи, когда вы строго думаете, что объект не имеет типа.

Длинный ответ

Идея заключается в методе Equals, который сравнивает два разных экземпляра объекта, чтобы определить, равны ли они на уровне значений.Однако конкретный тип определяет, как Equals метод должен быть реализован.Итеративного сравнения атрибутов, имеющих примитивные значения, может быть недостаточно; вполне могут существовать атрибуты, которые не следует считать частью значения объекта.Например,

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

В приведенном выше случае c на самом деле не важно определять, равны ли какие-либо два экземпляра MyClass, только a и b важные.В некоторых случаях c могут различаться в разных случаях, но не иметь существенного значения при сравнении.

Обратите внимание, что эта проблема возникает, когда члены сами могут быть экземплярами типа, и каждый из них должен иметь средства определения равенства.

Еще больше усложняет ситуацию то, что в JavaScript размыты различия между данными и методом.

Объект может ссылаться на метод, который должен быть вызван в качестве обработчика событий, и это, скорее всего, не будет считаться частью его «состояния значения».Тогда как другому объекту вполне может быть присвоена функция, выполняющая важные вычисления и тем самым отличающая этот экземпляр от других просто потому, что он ссылается на другую функцию.

А как насчет объекта, у которого один из существующих методов-прототипов переопределен другой функцией?Можно ли считать его равным другому экземпляру, который в остальном идентичен?На этот вопрос можно ответить только в каждом конкретном случае для каждого типа.

Как говорилось ранее, исключением будет строго бестиповый объект.В этом случае единственным разумным выбором является итеративное и рекурсивное сравнение каждого члена.Даже тогда приходится задаваться вопросом, каково «значение» функции?

Другие советы

Зачем изобретать велосипед?Давать Лодаш попытка.Он имеет ряд обязательных функций, таких как равно().

_.isEqual(object, other);

Он будет перебором проверять каждое значение ключа — как и другие примеры на этой странице — используя ECMAScript 5 и встроенные оптимизации, если они доступны в браузере.

Примечание:Ранее этот ответ рекомендовал Underscore.js, но Лодаш проделал лучшую работу по исправлению ошибок и последовательному решению проблем.

Оператор равенства по умолчанию в JavaScript для объектов возвращает значение true, когда они ссылаются на одно и то же место в памяти.

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

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

Если вам нужен другой оператор равенства, вам нужно будет добавить equals(other) метод или что-то подобное для ваших классов, и специфика вашей проблемной области будет определять, что именно это означает.

Вот пример игральной карты:

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

Если вы работаете в AngularJS, angular.equals функция определит, равны ли два объекта.В Эмбер.js использовать isEqual.

  • angular.equals - Посмотрите документы или источник подробнее об этом методе.Он также выполняет глубокое сравнение массивов.
  • Эмбер.js isEqual - Посмотрите документы или источник подробнее об этом методе.Он не выполняет глубокое сравнение массивов.

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>

Это моя версия.Он использует новые Объект.ключи функция, представленная в ES5, и идеи/тесты из +, + и +:

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 } }));

Если вы используете библиотеку JSON, вы можете закодировать каждый объект как JSON, а затем сравнить полученные строки на предмет равенства.

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

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

ПРИМЕЧАНИЕ:Хотя этот ответ будет работать во многих случаях, как отметили несколько человек в комментариях, он проблематичен по ряду причин.Практически во всех случаях вам потребуется найти более надежное решение.

Краткий функционал deepEqual выполнение:

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);
}

Редактировать:версия 2, используя предложение jib и функции стрелок ES6:

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);
}

Если у вас есть под рукой функция глубокого копирования, вы можете использовать следующий трюк, чтобы все еще использовать JSON.stringify при соответствии порядку свойств:

function equals(obj1, obj2) {
    function _equals(obj1, obj2) {
        return JSON.stringify(obj1)
            === JSON.stringify($.extend(true, {}, obj1, obj2));
    }
    return _equals(obj1, obj2) && _equals(obj2, obj1);
}

Демо: http://jsfiddle.net/CU3vb/3/

Обоснование:

Поскольку свойства obj1 копируются в клон по одному, их порядок в клоне сохранится.И когда свойства obj2 копируются в клон, поскольку свойства, уже существующие в obj1 будут просто перезаписаны, их заказы в клоне сохранятся.

Вы пытаетесь проверить, равны ли два объекта?то есть:их свойства равны?

Если это так, вы, вероятно, заметили такую ​​ситуацию:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

возможно, вам придется сделать что-то вроде этого:

function objectEquals(obj1, obj2) {
    for (var i in obj1) {
        if (obj1.hasOwnProperty(i)) {
            if (!obj2.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    for (var i in obj2) {
        if (obj2.hasOwnProperty(i)) {
            if (!obj1.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    return true;
}

Очевидно, что эта функция нуждается в некоторой оптимизации и возможности выполнять глубокую проверку (для обработки вложенных объектов: var a = { foo : { fu : "bar" } }) Но Вы получаете идею.

Как указал FOR, вам, возможно, придется адаптировать это для своих целей, например:разные классы могут иметь разные определения слова «равный».Если вы просто работаете с простыми объектами, описанного выше может быть достаточно, в противном случае можно использовать собственный MyClass.equals() функция может быть подходящим вариантом.

Самый простой и логичный решения для сравнения всего Объект, Массив, Строка, Целое...

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

Примечание:

  • вам нужно заменить val1и val2 с вашим объектом
  • для объекта вам нужно рекурсивно сортировать (по ключу) для обоих побочных объектов

В Node.js вы можете использовать его собственный require("assert").deepEqual.Больше информации:http://nodejs.org/api/assert.html

Например:

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

Другой пример, который возвращает true / false вместо возврата ошибок:

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;
};

я использую это comparable функция для создания копий моих объектов, сопоставимых с JSON:

var comparable = o => (typeof o != 'object' || !o)? o :
  Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});

// Demo:

var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };

console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>

Полезно при тестировании (большинство тестовых фреймворков имеют is функция).Например.

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

Если обнаружена разница, строки протоколируются, что делает различия видимыми:

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.

Вот решение в ES6/ES2015 с использованием функционального подхода:

const typeOf = x => 
  ({}).toString
      .call(x)
      .match(/\[object (\w+)\]/)[1]

function areSimilar(a, b) {
  const everyKey = f => Object.keys(a).every(f)

  switch(typeOf(a)) {
    case 'Array':
      return a.length === b.length &&
        everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
    case 'Object':
      return Object.keys(a).length === Object.keys(b).length &&
        everyKey(k => areSimilar(a[k], b[k]));
    default:
      return a === b;
  }
}

демо доступно здесь

Я не знаю, публиковал ли кто-нибудь что-нибудь подобное, но вот функция, которую я создал для проверки равенства объектов.

function objectsAreEqual(a, b) {
  for (var prop in a) {
    if (a.hasOwnProperty(prop)) {
      if (b.hasOwnProperty(prop)) {
        if (typeof a[prop] === 'object') {
          if (!objectsAreEqual(a[prop], b[prop])) return false;
        } else {
          if (a[prop] !== b[prop]) return false;
        }
      } else {
        return false;
      }
    }
  }
  return true;
}

Кроме того, он рекурсивный, поэтому он также может проверять глубокое равенство, если вы это так называете.

вы можете использовать _.isEqual(obj1, obj2) из библиотеки underscore.js.

Вот пример:

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

Официальную документацию смотрите здесь: http://underscorejs.org/#isEqual

Простое решение этой проблемы, о котором многие люди не понимают, — это сортировка строк JSON (по символам).Это также обычно быстрее, чем другие решения, упомянутые здесь:

function areEqual(obj1, obj2) {
    var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}

Еще одна полезная вещь в этом методе — вы можете фильтровать сравнения, передавая функцию «заменителя» функциям JSON.stringify (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter).Ниже будут сравниваться только все ключи объектов с именем «derp»:

function areEqual(obj1, obj2, filter) {
    var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
    return (key === 'derp') ? value : undefined;
});

Я бы посоветовал отказаться от хеширования или сериализации (как предлагает решение JSON).Если вам нужно проверить, равны ли два объекта, вам нужно определить, что означает «равно».Возможно, все элементы данных в обоих объектах совпадают, или это может быть так, что ячейки памяти должны совпадать (это означает, что обе переменные ссылаются на один и тот же объект в памяти), или может быть так, что только один элемент данных в каждом объекте должен совпадать.

Недавно я разработал объект, конструктор которого создает новый идентификатор (начиная с 1 и увеличиваясь на 1) каждый раз при создании экземпляра.Этот объект имеет функцию isEqual, которая сравнивает это значение идентификатора со значением идентификатора другого объекта и возвращает true, если они совпадают.

В этом случае я определил «равный» как означающий совпадение значений идентификаторов.Учитывая, что каждый экземпляр имеет уникальный идентификатор, это можно использовать для реализации идеи о том, что совпадающие объекты также занимают одну и ту же ячейку памяти.Хотя в этом нет необходимости.

Нуждаясь в более общей функции сравнения объектов, чем было опубликовано, я приготовил следующее.Критика оценила...

Object.prototype.equals = function(iObj) {
  if (this.constructor !== iObj.constructor)
    return false;
  var aMemberCount = 0;
  for (var a in this) {
    if (!this.hasOwnProperty(a))
      continue;
    if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
      return false;
    ++aMemberCount;
  }
  for (var a in iObj)
    if (iObj.hasOwnProperty(a))
      --aMemberCount;
  return aMemberCount ? false : true;
}

Если вы сравниваете объекты JSON, вы можете использовать https://github.com/mirek/node-rus-diff

npm install rus-diff

Использование:

a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}

var rusDiff = require('rus-diff').rusDiff

console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

Если два объекта различны, значит, они совместимы с MongoDB. {$rename:{...}, $unset:{...}, $set:{...}} подобный объект возвращается.

Я столкнулся с той же проблемой и решил написать собственное решение.Но поскольку я хочу также сравнить массивы с объектами и наоборот, я разработал общее решение.Я решил добавить функции в прототип, но их можно легко переписать в автономные функции.Вот код:

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

Этот алгоритм разделен на две части;Сама функция равенства и функция для поиска числового индекса свойства в массиве/объекте.Функция find необходима только потому, что indexof находит только числа и строки, а не объекты.

Это можно назвать так:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

Функция возвращает либо true, либо false, в данном случае true.Алгоритм также позволяет сравнивать очень сложные объекты:

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

Верхний пример вернет true, даже если свойства имеют другой порядок.Одна маленькая деталь, на которую стоит обратить внимание:Этот код также проверяет наличие двух переменных одного и того же типа, поэтому «3» — это не то же самое, что 3.

Просто хотел поделиться своей версией сравнения объектов с использованием некоторых функций es6.Заказ не учитывается.После преобразования всех if/else в троичные я получил следующее:

function areEqual(obj1, obj2) {

    return Object.keys(obj1).every(key => {

            return obj2.hasOwnProperty(key) ?
                typeof obj1[key] === 'object' ?
                    areEqual(obj1[key], obj2[key]) :
                obj1[key] === obj2[key] :
                false;

        }
    )
}

Полезно считать два объекта равными, если они имеют одинаковые значения для всех свойств и рекурсивно для всех вложенных объектов и массивов.Я также считаю равными следующие два объекта:

var a = {p1: 1};
var b = {p1: 1, p2: undefined};

Точно так же массивы могут иметь «отсутствующие» элементы и неопределенные элементы.Я бы отнесся к ним так же:

var c = [1, 2];
var d = [1, 2, undefined];

Функция, реализующая это определение равенства:

function isEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (generalType(a) != generalType(b)) {
        return false;
    }

    if (a == b) {
        return true;
    }

    if (typeof a != 'object') {
        return false;
    }

    // null != {}
    if (a instanceof Object != b instanceof Object) {
        return false;
    }

    if (a instanceof Date || b instanceof Date) {
        if (a instanceof Date != b instanceof Date ||
            a.getTime() != b.getTime()) {
            return false;
        }
    }

    var allKeys = [].concat(keys(a), keys(b));
    uniqueArray(allKeys);

    for (var i = 0; i < allKeys.length; i++) {
        var prop = allKeys[i];
        if (!isEqual(a[prop], b[prop])) {
            return false;
        }
    }
    return true;
}

Исходный код (включая вспомогательные функции GeneralType и uniqueArray):Модульный тест и Тестовый раннер здесь.

С этой функцией я делаю следующие предположения:

  1. Вы управляете объектами, которые сравниваете, и у вас есть только примитивные значения (т.не вложенные объекты, функции и т. д.).
  2. Ваш браузер поддерживает Объект.ключи.

К этому следует относиться как к демонстрации простой стратегии.

/**
 * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
 * @param {Object} object1
 * @param {Object} object2
 * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
 * @returns {Boolean}
 */
function isEqual( object1, object2, order_matters ) {
    var keys1 = Object.keys(object1),
        keys2 = Object.keys(object2),
        i, key;

    // Test 1: Same number of elements
    if( keys1.length != keys2.length ) {
        return false;
    }

    // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
    // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
    // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
    // This is why we are sorting keys1 and keys2.
    if( !order_matters ) {
        keys1.sort();
        keys2.sort();
    }

    // Test 2: Same keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}

Это дополнение ко всему вышеперечисленному, а не замена.Если вам нужно быстро выполнить поверхностное сравнение объектов без необходимости проверять дополнительные рекурсивные случаи.Вот выстрел.

Это можно сравнить с:1) Равенство количества собственных свойств, 2) Равенство имен ключей, 3) если bCompareValues ​​== true, Равенство соответствующих значений свойств и их типов (тройное равенство)

var shallowCompareObjects = function(o1, o2, bCompareValues) {
    var s, 
        n1 = 0,
        n2 = 0,
        b  = true;

    for (s in o1) { n1 ++; }
    for (s in o2) { 
        if (!o1.hasOwnProperty(s)) {
            b = false;
            break;
        }
        if (bCompareValues && o1[s] !== o2[s]) {
            b = false;
            break;
        }
        n2 ++;
    }
    return b && n1 == n2;
}

Для сравнения ключей для простых экземпляров объектов пар ключ/значение я использую:

function compareKeys(r1, r2) {
    var nloops = 0, score = 0;
    for(k1 in r1) {
        for(k2 in r2) {
            nloops++;
            if(k1 == k2)
                score++; 
        }
    }
    return nloops == (score * score);
};

После сравнения ключей выполняется простая дополнительная процедура. for..in петли достаточно.

Сложность равна O(N*N), где N — количество ключей.

Я надеюсь/предполагаю, что объекты, которые я определяю, не будут содержать более 1000 свойств...

Я знаю, что это немного устарело, но я хотел бы добавить решение, которое я придумал для этой проблемы.У меня был объект, и я хотел знать, когда его данные изменились.«что-то похожее на Object.observe», и я сделал следующее:

function checkObjects(obj,obj2){
   var values = [];
   var keys = [];
   keys = Object.keys(obj);
   keys.forEach(function(key){
      values.push(key);
   });
   var values2 = [];
   var keys2 = [];
   keys2 = Object.keys(obj2);
   keys2.forEach(function(key){
      values2.push(key);
   });
   return (values == values2 && keys == keys2)
}

Это здесь можно продублировать и создать другой набор массивов для сравнения значений и ключей.Это очень просто, потому что теперь они представляют собой массивы и возвращают false, если объекты имеют разные размеры.

Выдергиваю из своей личной библиотеки, которую неоднократно использую в работе.Следующая функция представляет собой мягкое рекурсивное глубокое равенство, которое не проверяет

  • Классовое равенство
  • Унаследованные ценности
  • Ценит строгое равенство

В основном я использую это, чтобы проверить, получаю ли я одинаковые ответы на различные реализации API.Могут возникнуть различия в реализации (например, строка или число) и дополнительные нулевые значения.

Его реализация довольно проста и коротка (если убрать все комментарии)

/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable 
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
***   most probable cases of == / JSON equality. Consider bench testing, if you have
***   more 'complex' requirments
***
*** @param  objA : First object to compare
*** @param  objB : 2nd object to compare
*** @param  .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by eugene@picoded.com, 2012.
***          Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
	// Multiple comparision check
	//--------------------------------------------
	var args = Array.prototype.slice.call(arguments);
	if(args.length > 2) {
		for(var a=1; a<args.length; ++a) {
			if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
				return false;
			}
		}
		return true;
	} else if(args.length < 2) {
		throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
	}
	
	// basic equality check,
	//--------------------------------------------
	// if this succed the 2 basic values is equal,
	// such as numbers and string.
	//
	// or its actually the same object pointer. Bam
	//
	// Note that if string and number strictly equal is required
	// change the equality from ==, to ===
	//
	if(objA == objB) {
		return true;
	}
	
	// If a value is a bsic type, and failed above. This fails
	var basicTypes = ["boolean", "number", "string"];
	if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
		return false;
	}
	
	// JSON equality check,
	//--------------------------------------------
	// this can fail, if the JSON stringify the objects in the wrong order
	// for example the following may fail, due to different string order:
	//
	// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
	//
	if(JSON.stringify(objA) == JSON.stringify(objB)) {
		return true;
	}
	
	// Array equality check
	//--------------------------------------------
	// This is performed prior to iteration check,
	// Without this check the following would have been considered valid
	//
	// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
	//
	// Note that u may remove this segment if this is what is intended
	//
	if( Array.isArray(objA) ) {
		//objA is array, objB is not an array
		if( !Array.isArray(objB) ) {
			return false;
		}
	} else if( Array.isArray(objB) ) {
		//objA is not array, objB is an array
		return false;
	}
	
	// Nested values iteration
	//--------------------------------------------
	// Scan and iterate all the nested values, and check for non equal values recusively
	//
	// Note that this does not check against null equality, remove the various "!= null"
	// if this is required
	
	var i; //reuse var to iterate
	
	// Check objA values against objB
	for (i in objA) {
		//Protect against inherited properties
		if(objA.hasOwnProperty(i)) {
			if(objB.hasOwnProperty(i)) {
				// Check if deep equal is valid
				if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
					return false;
				}
			} else if(objA[i] != null) {
				//ignore null values in objA, that objB does not have
				//else fails
				return false;
			}
		}
	}
	
	// Check if objB has additional values, that objA do not, fail if so
	for (i in objB) {
		if(objB.hasOwnProperty(i)) {
			if(objB[i] != null && !objA.hasOwnProperty(i)) {
				//ignore null values in objB, that objA does not have
				//else fails
				return false;
			}
		}
	}
	
	// End of all checks
	//--------------------------------------------
	// By reaching here, all iteration scans have been done.
	// and should have returned false if it failed
	return true;
}

// Sanity checking of simpleRecusiveDeepEqual
(function() {
	if(
		// Basic checks
		!simpleRecusiveDeepEqual({}, {}) ||
		!simpleRecusiveDeepEqual([], []) ||
		!simpleRecusiveDeepEqual(['a'], ['a']) ||
		// Not strict checks
		!simpleRecusiveDeepEqual("1", 1) ||
		// Multiple objects check
		!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
		// Ensure distinction between array and object (the following should fail)
		simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
		// Null strict checks
		simpleRecusiveDeepEqual( 0, null ) ||
		simpleRecusiveDeepEqual( "", null ) ||
		// Last "false" exists to make the various check above easy to comment in/out
		false
	) {
		alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
	} else { 
		//added this last line, for SO snippet alert on success
		alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
	}
})();

Вот версия трюка stringify, которая требует меньше ввода и работает во многих случаях для тривиальных сравнений данных JSON.

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }

Я вижу ответы по коду спагетти.Без использования каких-либо сторонних библиотек это очень просто.

Сначала отсортируйте два объекта по ключам и их именам.

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

Затем просто используйте строку для их сравнения.

JSON.stringify(objectOne) === JSON.stringify(objectTwo)

Для тех из вас, кто использует NodeJS, есть удобный метод под названием isDeepStrictEqual в собственной библиотеке Util, которая может этого добиться.

const util = require('util');

const foo = {
  hey: "ho",
  lets: "go"
}

const bar = {
  hey: "ho",
  lets: "go"
}

foo == bar // false
util.isDeepStrictEqual(foo, bar) // true

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top