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

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

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

Ответ 1

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

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

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

Понятие представляет собой метод 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

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

_.isEqual(object, other);

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

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

Ответ 3

Оператор равенства по умолчанию в 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

Ответ 4

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

  • angular.equals - см. docs или источник больше для этого метода. Он также делает глубокое сравнение с массивами.
  • Ember.js isEqual - см. docs или source для получения дополнительной информации об этом методе. Это не делает глубокое сравнение массивов.

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>

Ответ 5

Это моя версия. Он использует новую функцию Object.keys, которая вводится в 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 } }));

Ответ 6

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

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

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

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

Ответ 7

Краткая функциональная реализация 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 arrow:

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

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

Простейшие и логические решения для сравнения всего, как Object, Array, String, Int...

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

Заметка:

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

Ответ 11

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

Ответ 12

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

Ответ 13

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

демо здесь

Ответ 14

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

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

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

Ответ 15

вы можете использовать _.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

Ответ 16

Предполагая, что порядок свойств в объекте не изменился.

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

var object1 = {
  key: "value"
};

var object2 = {
  key: "value"
};

var object3 = {
  key: "no value"
};

console.log('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));

console.log('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));

Ответ 17

Простое решение этой проблемы, о котором многие не подозревают, - это сортировка строк 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). Далее будут сравниваться все ключи объектов, которые называются "сумасшедший":

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

Ответ 19

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

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

Ответ 20

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

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

Ответ 21

Если вы сравниваете объекты 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:{...}}.

Ответ 22

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

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. Алгоритм позволяет сравнивать между очень сложными объектами:

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

Ответ 23

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

        }
    )
}

Ответ 24

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

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

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)

Ответ 25

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

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

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

Ответ 26

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

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

Аналогично, массивы могут иметь "отсутствующие" элементы и 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): Unit Test и Test Runner здесь.

Ответ 27

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

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

Это следует рассматривать как демонстрацию простой стратегии.

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

Ответ 28

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

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

Ответ 29

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

Это сравнивается для: 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;
}

Ответ 30

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