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

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


当前回答

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

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

而且,它是递归的,所以它也可以检查深度相等,如果你这么称呼它的话。

其他回答

如何确定部分对象(partial <T>)等于typescript中的原始对象(T)。

function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
  return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}

附注:最初我打算提出一个有答案的新问题。但这样的问题已经存在,并被标记为重复题。

这个问题已经有30多个答案了。我将总结并解释它们(用“我父亲”的比喻),并添加我建议的解决方案。

你有4+1类解


1)使用不完整的简单语句

如果你赶时间,那么99%的正确性都可以。

例如,Pratik Bhalodiya建议的JSON.stringify()或JSON。encode,或. tostring(),或其他方法将对象转换为String,然后逐个字符使用===比较两个String。

然而,缺点是在String中没有Object的全局标准唯一表示。例如:{a: 5, b: 8}和{b: 8和a: 5}是相等的。

优点:快,快。 缺点:希望有效!如果环境/浏览器/引擎记住了对象的顺序(例如Chrome/V8),并且键的顺序不同(感谢Eksapsy),它将无法工作。所以,完全不能保证。在大型对象中,性能也不会很好。

我的父亲类比

当我在谈论我的父亲时,“我高大英俊的父亲”和“我高大英俊的父亲”是同一个人!但这两个弦是不一样的。

请注意,在英语语法中,形容词的顺序其实是正确的(标准的方式),它应该是“英俊的高个子男人”,但如果你盲目地认为iOS 8 Safari的Javascript引擎也遵循同样的语法,你就是在拿自己的能力冒险!# WelcomeToJavascriptNonStandards


2)自己DIY编写递归函数

如果你正在学习,这很好。

例子是atmin的解。

最大的缺点是你肯定会错过一些边缘情况。您考虑过对象值中的自引用吗?你考虑过NaN吗?您是否考虑过具有相同ownProperties但不同原型父对象的两个对象?

我只鼓励人们在进行实践并且代码不会投入生产的情况下这样做。这是唯一一种重新发明轮子有正当理由的情况。

优点:学习机会。 缺点:不可靠。需要时间和精力。

我的父亲类比

这就像假设我爸爸的名字是“约翰·史密斯”,他的生日是“1/1/1970”,那么任何一个名字是“约翰·史密斯”,出生在“1/1/1970”的人就是我的父亲。

通常情况下是这样,但如果有两个“约翰·史密斯”在那一天出生呢?如果你认为你会考虑他们的身高,那么这就提高了准确性,但仍然不是一个完美的比较。

2.1你的范围有限DIY比较器

与其疯狂地递归检查所有属性,不如考虑只检查“有限”数量的属性。例如,如果对象是Users,您可以比较它们的emailAddress字段。

它仍然不是一个完美的解决方案,但比解决方案#2的好处是:

它是可预测的,而且不太可能崩溃。 您正在推动平等的“定义”,而不是依赖于对象的原始形式和形状及其原型和嵌套属性。


3)使用功能相同的图书馆版本

如果您需要生产级别的质量,并且您不能更改系统的设计,则很好。

例如_。等于lodash,已经在coolaj86的回答中,或者在Tony Harvey的回答中提到的Angular的回答中,或者在Node的Rafael Xavier的回答中。

优点:其他人都这么做。 缺点:外部依赖,这可能会花费你额外的内存/CPU/安全问题,甚至一点点。此外,仍然可以错过一些边缘情况(例如,是否两个对象具有相同的ownProperties但不同的原型父应该被认为是相同的。)最后,你可能无意中解决了一个潜在的设计问题;只是说!

我的父亲类比

这就像付钱给中介机构,根据他的电话、姓名、地址等找到我的生父。

这会花更多的钱,而且可能比我做背景调查更准确,但不能涵盖边缘情况,比如我父亲是移民/庇护者,他的生日是未知的!


4)在对象中使用标识符

如果你仍然可以改变系统(你正在处理的对象)的设计,并且你希望你的代码能够持久,那就太好了。

它并不适用于所有情况,而且性能可能不太好。然而,这是一个非常可靠的解决方案,如果你能做到的话。

解决方案是,系统中的每个对象都有一个唯一的标识符以及所有其他属性。标识符的唯一性将在生成时得到保证。在比较两个对象时,您将使用这个ID(也称为UUID/GUID—全局/通用唯一标识符)。即,当且仅当这些id相等时,它们相等。

id可以是简单的自动增量数字,也可以是通过库生成的字符串(建议)或一段代码。所有你需要做的是确保它总是唯一的,在auto_incremental的情况下,它可以是内置的,或者在UUID的情况下,可以检查是否所有现有的值(例如MySQL的unique列属性)或简单地(如果来自库)依赖于给出极低的冲突可能性。

注意,您还需要始终将ID存储在对象中(以保证其唯一性),并且实时计算它可能不是最好的方法。

优点:可靠,高效,不脏,现代。 缺点:需要额外的空间。可能需要重新设计一下系统。

我的父亲类比

我父亲的社保号是911-345-9283,所以有这个社保号的人就是我父亲,任何自称是我父亲的人也一定有这个社保号。


结论

就准确性和可靠性而言,我个人更喜欢解决方案#4 (ID)。如果不可能,我会选择2.1,因为它具有可预测性,然后是3。如果两者都不可能,选择第二条,最后是第一条。

Const obj = { 名称:“卡尔”, 年龄:15 } Const obj2 = { 名称:“卡尔”, 年龄:15日 } const compareObj = (objects) => { Const res = objects.map((item) => { 返回Object.entries(项).flat . join () () }) 返回res.every((a) => { 返回a === res[0] }) } console.log (compareObj (obj, methoda ()))

2022:

我想出了一个非常简单的算法来解决大多数边缘情况。

步骤:

使物体变平 简单地比较两个扁平的物体并寻找差异

如果你保存了平面对象,你可以重复使用它。

let obj1= {var1:'value1', var2:{ var1:'value1', var2:'value2'}};
let obj2 = {var1:'value1', var2:{ var1:'value11',var2:'value2'}} 

let flat1= flattenObject(obj1)
/*
{
 'var1':'value1',
 'var2.var1':'value1',
 'var2.var2':'value2'
}
*/
let flat2= flattenObject(obj2)
/*
{
 'var1':'value1',
 'var2.var1':'value11',
 'var2.var2':'value2'
}
*/
isEqual(flat1, flat2)
/*
 false
*/

当然,您可以为这些步骤提供您的实现。但我的想法是:

实现

function flattenObject(obj) {
 const object = Object.create(null);
 const path = [];
 const isObject = (value) => Object(value) === value;

 function dig(obj) {
  for (let [key, value] of Object.entries(obj)) {
    path.push(key);
    if (isObject(value)) dig(value);
    else object[path.join('.')] = value;
    path.pop();
  }
 }

 dig(obj);
 return object;
}
 function isEqual(flat1, flat2) {
    for (let key in flat2) {
        if (flat1[key] !== flat2[key])
            return false
    }
    // check for missing keys
    for (let key in flat1) {
        if (!(key in flat2))
            return false
    }
    return true
}

你也可以使用这个方法来获取obj1和obj2之间的Diff对象。

看看这个答案的细节:两个对象之间的一般深度差异

这是一个非常干净的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"})