Как определить равенство для двух объектов JavaScript? - PullRequest
547 голосов
/ 14 октября 2008

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

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

Ответы [ 55 ]

0 голосов
/ 28 мая 2013

Вот довольно чистая CoffeeScript версия того, как вы могли бы сделать это:

Object::equals = (other) ->
  typeOf = Object::toString

  return false if typeOf.call(this) isnt typeOf.call(other)
  return `this == other` unless typeOf.call(other) is '[object Object]' or
                                typeOf.call(other) is '[object Array]'

  (return false unless this[key].equals other[key]) for key, value of this
  (return false if typeof this[key] is 'undefined') for key of other

  true

Вот тесты:

  describe "equals", ->

    it "should consider two numbers to be equal", ->
      assert 5.equals(5)

    it "should consider two empty objects to be equal", ->
      assert {}.equals({})

    it "should consider two objects with one key to be equal", ->
      assert {a: "banana"}.equals {a: "banana"}

    it "should consider two objects with keys in different orders to be equal", ->
      assert {a: "banana", kendall: "garrus"}.equals {kendall: "garrus", a: "banana"}

    it "should consider two objects with nested objects to be equal", ->
      assert {a: {fruit: "banana"}}.equals {a: {fruit: "banana"}}

    it "should consider two objects with nested objects that are jumbled to be equal", ->
      assert {a: {a: "banana", kendall: "garrus"}}.equals {a: {kendall: "garrus", a: "banana"}}

    it "should consider two objects with arrays as values to be equal", ->
      assert {a: ["apple", "banana"]}.equals {a: ["apple", "banana"]}



    it "should not consider an object to be equal to null", ->
      assert !({a: "banana"}.equals null)

    it "should not consider two objects with different keys to be equal", ->
      assert !({a: "banana"}.equals {})

    it "should not consider two objects with different values to be equal", ->
      assert !({a: "banana"}.equals {a: "grapefruit"})
0 голосов
/ 12 января 2010

Мне нужно смоделировать JQuery POST-запросы, поэтому для меня важно то, что оба объекта имеют одинаковый набор свойств (ни один из них отсутствует) и что каждое значение свойства «равно» (согласно этому определению) ). Меня не волнуют объекты, у которых есть несовпадающие методы.

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

function PostRequest() {
    for (var i = 0; i < arguments.length; i += 2) {
        this[arguments[i]] = arguments[i+1];
    }

    var compare = function(u, v) {
        if (typeof(u) != typeof(v)) {
            return false;
        }

        var allkeys = {};
        for (var i in u) {
            allkeys[i] = 1;
        }
        for (var i in v) {
            allkeys[i] = 1;
        }
        for (var i in allkeys) {
            if (u.hasOwnProperty(i) != v.hasOwnProperty(i)) {
                if ((u.hasOwnProperty(i) && typeof(u[i]) == 'function') ||
                    (v.hasOwnProperty(i) && typeof(v[i]) == 'function')) {
                    continue;
                } else {
                    return false;
                }
            }
            if (typeof(u[i]) != typeof(v[i])) {
                return false;
            }
            if (typeof(u[i]) == 'object') {
                if (!compare(u[i], v[i])) {
                    return false;
                }
            } else {
                if (u[i] !== v[i]) {
                    return false;
                }
            }
        }

        return true;
    };

    this.equals = function(o) {
        return compare(this, o);
    };

    return this;
}

Используйте вот так:

foo = new PostRequest('text', 'hello', 'html', '<p>hello</p>');
foo.equals({ html: '<p>hello</p>', text: 'hello' });
0 голосов
/ 14 января 2018

Здесь много хороших мыслей! Вот моя версия глубокого равноправия. Я разместил его на github и написал несколько тестов. Трудно охватить все возможные случаи, а иногда нет необходимости делать это.

Я охватил NaN !== NaN, а также круговые зависимости.

https://github.com/ryancat/simple-deep-equal/blob/master/index.js

0 голосов
/ 04 мая 2019

Это приемлемо?

deepEqual = (x, y) => {
 let areEqual = false;
 const Obj = Object.keys(x);
 const keysSize = Obj.length;
 let counter = 0;
 Obj.forEach(key => {
  if (y[key] === x[key]) {
    counter += 1;
  }
 });
 if (counter === keysSize) areEqual = true;
 return areEqual;
};
0 голосов
/ 29 июля 2016

У меня есть намного более короткая функция, которая углубится во все подобъекты или массивы. Это так же эффективно, как JSON.stringify(obj1) === JSON.stringify(obj2), но JSON.stringify не будет работать, если порядок не такой (, как указано здесь ).

var obj1 = { a : 1, b : 2 };
var obj2 = { b : 2, a : 1 };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false

Функция также будет хорошим началом, если вы хотите что-то сделать с неравными значениями.

function arr_or_obj(v)
{ return !!v && (v.constructor === Object || v.constructor === Array); }

function deep_equal(v1, v2)
{
    if (arr_or_obj(v1) && arr_or_obj(v2) && v1.constructor === v2.constructor)
    {
        if (Object.keys(v1).length === Object.keys(v2).length) // check the length
        for (var i in v1)
        {
            if (!deep_equal(v1[i], v2[i]))
            { return false; }
        }
        else
        { return false; }
    }
    else if (v1 !== v2)
    { return false; }

    return true;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

var obj1 = [
    {
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        },
        shoes : [ 'loafer', 'penny' ]
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

var obj2 = [
    {
        shoes : [ 'loafer', 'penny' ], // same even if the order is different
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        }
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

console.log(deep_equal(obj1, obj2)); // true
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false
console.log(deep_equal([], [])); // true
console.log(deep_equal({}, {})); // true
console.log(deep_equal([], {})); // false

И если вы хотите добавить поддержку Function, Date и RegExp, вы можете добавить это в начале deep_equal (не тестировалось):

if ((typeof obj1 === 'function' && typeof obj2 === 'function') ||
(obj1 instanceof Date && obj2 instanceof Date) ||
(obj1 instanceof RegExp && obj2 instanceof RegExp))
{
    obj1 = obj1.toString();
    obj2 = obj2.toString();
}
...