Как определить равенство для двух объектов JavaScript?

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

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

30 ответов


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

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

долгий ответ

концепция-это метод 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 различие между данными и методом размыто.

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

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

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


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

_.isEqual(object, other);

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

Примечание: ранее этот ответ рекомендовал подчеркивания.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 - см. docs или источник подробнее об этом методе. Он также делает глубокое сравнение на массивах.
  • Эмбер.js isEqual - см. docs или источник подробнее об этом методе. Это не делает глубокое сравнение на матрицы.

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>

Это моя версия. Он использует new функция, которая вводится в 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, используя предложение Кливера и функции стрелки 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 будут просто перезаписаны, их заказы в Клоне будут сохранены.


В Узел.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;
};

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

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

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" } }), но вы получаете идею.

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


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

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

Примечание:

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

Я использую этот 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>

пригодится в тестах (большинство тестовых фреймворков имеют


Heres-решение в 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;
  }
}

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


простым решением этой проблемы, которое многие люди не понимают, является сортировка строк 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(''));
}

еще одна полезная вещь об этом методе - вы можете фильтровать сравнения, передавая функцию "replacer" в 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;
});

можно использовать _.isEqual(obj1, obj2) от подчеркивания.библиотека 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


Если вы используете ES6+ via Babel или иначе, вы также можете использовать Object.is(x, y).

ссылка:http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y


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

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

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


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

недавно я разработал объект, конструктор которого создает новый идентификатор (начиная с 1 и увеличивая на 1)каждый раз, когда создается экземпляр. Этот объект имеет функцию isEqual, которая сравнивает это значение id со значением id другого объекта и возвращает 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:{...}} возвращается объект like.


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

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

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

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

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

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

({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.


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

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 свойств...


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

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. Где может возникнуть разница в реализации (например, string vs number) и дополнительные нулевые значения.

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

/** 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!");
	}
})();

просто хотел внести свой вклад в мою версию сравнения объектов, используя некоторые функции 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;

        }
    )
}

вот версия трюка 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 { ... }

для этого есть очень простое исправление, все, что вам нужно сделать, это JSON.stringify () для обоих объектов при сравнении двух.