严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?

堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。


当前回答

在对象中(没有方法),我们需要检查嵌套的对象,数组和基本类型。对象可以有其他对象和数组(数组也可以包括其他对象和数组),所以我们可以使用如下所示的递归函数:arrayEquals检查数组是否相等,equals检查对象是否相等:

function arrayEquals(a, b) {
    if (a.length != b.length) {
        return false;
    }
    for (let i = 0; i < a.length; i++) {
        if (a[i].constructor !== b[i].constructor) {
            return false;
        }

        if (a[i] instanceof Array && b[i] instanceof Array) {
            if (!arrayEquals(a, b)) {
                return false;
            }
        } else if (a[i] instanceof Object && b[i] instanceof Object) {
            if (!equals(a[i], b[i])) {
                return false;
            }
        } else if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
}

function equals(a, b) {
    for (let el in a) {
        if (b.hasOwnProperty(el)) {
            if (a[el].constructor !== b[el].constructor) {
                return false;
            }

            if (a[el] instanceof Array && b[el] instanceof Array) {
                if (!arrayEquals(a[el], b[el])) {
                    return false;
                }
            } else if (a[el] instanceof Object && b[el] instanceof Object) {
                if (!equals(a[el], b[el])) {
                    return false;
                }
            } else if (a[el] !== b[el]) {
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}

假设你有两个对象:

let a = {
    a: 1,
    b: { c: 1, d: "test" },
    c: 3,
    d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};

let b = {
    a: 1,
    b: { c: 1, d: "test" },
    c: 3,
    d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};

在这里使用上面的equals函数,你可以很容易地比较这两个对象,像这样:

if(equals(a, b)) {
    // do whatever you want
}

其他回答

这取决于你对平等的定义。因此,作为类的开发人员,要由您来定义它们的相等性。

有时会使用一种情况,如果两个实例指向内存中的相同位置,则认为它们是“相等的”,但这并不总是您想要的。例如,如果我有一个Person类,如果两个Person对象具有相同的Last Name、First Name和Social Security Number(即使它们指向内存中的不同位置),我可能会认为它们是“相等的”。

另一方面,我们不能简单地说两个对象是相等的,如果它们的每个成员的值都相同,因为,有时,你并不想这样。换句话说,对于每个类,由类开发人员定义组成对象“标识”的成员并开发适当的相等操作符(通过重载==操作符或Equals方法)。

Saying that two objects are equal if they have the same hash is one way out. However you then have to wonder how the hash is calculated for each instance. Going back to the Person example above, we could use this system if the hash was calculated by looking at the values of the First Name, Last Name, and Social Security Number fields. On top of that, we are then relying on the quality of the hashing method (that's a huge topic on its own, but suffice it to say that not all hashes are created equal, and bad hashing methods can lead to more collisions, which in this case would return false matches).

简单来说:

const compare = (x, y) => {
  const srt = (obj) => JSON.stringify(obj)?.split('').sort().join('');
  return srt(x) === srt(y);
};

// ----- How to use ---
const a = {'one':1, 'two':2,'three':3};
const b = {'two':2, 'one':1, 'three':3}; //note same values as (const a)
const c = {'one':1, 'two':2,'three':3};
const d = {'one':1, 'two':2,'four':4};

compare(a, b); //true
compare(a, c); //true
compare(a, d); //false

//----- BUT! -----
JSON.stringify(a) === JSON.stringify(b); //false

//----- AND -----
compare({}, {}); //true
compare({}, undefined); //false
compare(undefined, undefined); //true
compare(undefined, ''); //false
compare(undefined, null); //false
compare(null, null); //true
compare('', ''); //true

我有一个更短的函数,它将深入到所有子对象或数组。它和JSON.stringify(obj1) === JSON.stringify(obj2)一样高效,但是JSON.stringify(obj2)。如果顺序不相同(如此处所述),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();
}

我不知道是否有人发布过类似的东西,但这是我做的一个检查对象相等的函数。

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。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中已经存在的属性将被简单地覆盖,它们在克隆对象中的顺序将被保留。