如何确定两个JavaScript对象的相等性?

599 javascript equals object hashcode

严格的相等运算符将告诉您两个对象类型是否相等.但是,有没有办法判断两个对象是否相等,就像 Java中的哈希码一样

Stack Overflow问题JavaScript中是否有任何类型的hashCode函数?类似于这个问题,但需要更多的学术答案.上面的场景说明了为什么需要有一个,我想知道是否有任何等效的解决方案.

Coo*_*J86 479

为什么重新发明轮子?给Lodash一试.它有许多必备功能,例如isEqual().

_.isEqual(object, other);
Run Code Online (Sandbox Code Playgroud)

它将强制检查每个键值 - 就像本页上的其他示例一样 - 使用ECMAScript 5和本机优化(如果它们在浏览器中可用).

注:以前这个答案推荐Underscore.js,但lodash做得越来越修复的错误,并与一致性解决问题的一个更好的工作.

  • 如果你看看下划线给你的东西,你不会后悔拉进去 (27认同)
  • Underscore的isEqual函数是非常好的(但你必须拉他们的库来使用它 - 约3K gzip压缩). (24认同)
  • 有一个Underscore的分支称为[LoDash](http://lodash.com),该作者非常关注诸如此类的一致性问题.使用LoDash进行测试,看看你得到了什么. (6认同)
  • 即使您不能将下划线作为依赖项,也请将isEqual函数拉出来,满足许可证要求并继续.到目前为止,它是stackoverflow上提到的最全面的相等测试. (5认同)
  • @mckoss如果你不想要整个库,你可以使用独立模块https://www.npmjs.com/package/lodash.isequal (5认同)
  • 怎么说“哦,就用X包吧!” 有帮助吗?你告诉这个人抽象和批量他们的代码,而没有真正解释如何真正找到解决方案。我并不是说不要使用包或建议它们,但 JavaScript 生态系统是变化无常的,你应该促进对解决方案的实际理解,而不仅仅是一种暂时绕过它的方法。 (3认同)
  • 只是在工作中讨论这个问题和_的问题是_.isEqual(0,-0)=== false和_.isEqual(NaN,NaN)=== true - 这至少对我来说是违背意外的. (2认同)
  • Underscore.js 的 isEqual 实现在两个具有完全相同属性的对象上失败(如 Firebug 中所示并使用 JSON.stringify 进行测试),如下所示: `{"from":70,"to":[ 1283,1301],“主题”:“测试”,“主体”:“测试”,“bodyType”:“文本”,“优先级”:“高”}` (2认同)
  • 如果您使用 webpack 和 ES6,则只需导入您想要使用的 lodash 函数即可。例如`import { isEqual } from 'lodash'`。然后你的捆绑 JS 将只包含该函数,并跳过你不使用的所有内容。 (2认同)
  • 还要考虑[deep-equal](https://www.npmjs.com/package/deep-equal)包,它甚至适用于对象数组. (2认同)

Ant*_*nes 168

简短的回答

简单的答案是:不,没有通用的方法来确定一个对象在你的意思上是否等于另一个.例外情况是您严格考虑对象是无类型的.

答案很长

概念是Equals方法,它比较对象的两个不同实例,以指示它们在值级别上是否相等.但是,由特定类型决定如何Equals实现方法.具有原始值的属性的迭代比较可能是不够的,可能存在不被视为对象值的一部分的属性.例如,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,c确定MyClass的任何两个实例是否相等是唯一ab重要的并不重要.在某些情况下c,实例之间可能会有所不同,但在比较期间却不显着.

请注意,当成员本身也可以是类型的实例时,此问题适用,并且这些实例都需要具有确定相等性的方法.

更复杂的是,在JavaScript中,数据和方法之间的区别是模糊的.

一个对象可以引用一个被称为事件处理程序的方法,这可能不会被视为其"值状态"的一部分.而另一个对象可能被赋予执行重要计算的功能,从而使该实例与其他对象不同,仅仅因为它引用了不同的功能.

如果某个对象的某个现有原型方法被另一个函数覆盖了?是否仍然可以认为它与其他实例相同?这个问题只能在每种类型的每个特定情况下得到解答.

如前所述,异常将是严格无类型的对象.在这种情况下,唯一明智的选择是每个成员的迭代和递归比较.即使这样,人们也要问一个函数的"价值"是什么?

  • 如果你使用下划线,你可以做`_.isEqual(obj1,obj2);` (170认同)
  • 这个解释很好,它没有给出任何具体的解决方案. (71认同)
  • @Harsh,答案没有提供任何解决方案,因为没有.即使在Java中,对象相等性比较没有灵丹妙药,并且正确实现`.equals`方法并不简单,这就是为什么有效的Java*中有这样一个主题的原因. (10认同)
  • @Kumar Harsh,使两个对象相等的是非常特定于应用程序; 并非一定要考虑对象的每个属性,因此强制对象的每个属性也不是具体的解决方案. (3认同)

Dan*_*ore 148

JavaScript for Objects中的默认相等运算符在引用内存中的相同位置时产生true.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true
Run Code Online (Sandbox Code Playgroud)

如果你需要一个不同的相等运算符,你需要在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
Run Code Online (Sandbox Code Playgroud)

  • @scotts转换为JSON的其他问题是字符串中属性的顺序变得很重要.`{x:1,y:2}`!==`{y:2,x:1}` (7认同)
  • @scotts并非总是如此.将对象转换为JSON并比较字符串对于紧密循环中的复杂对象而言可能变得计算密集.对于简单的物体,它可能并不重要,但实际上它实际上取决于您的具体情况.正确的解决方案可能与比较对象ID或检查每个属性一样简单,但其正确性完全由问题域决定. (3认同)

Tro*_*vey 73

如果您在AngularJS中工作,该angular.equals函数将确定两个对象是否相等.在Ember.js中使用isEqual.

  • angular.equals- 有关此方法的更多信息,请参阅文档来源.它也对数组进行了深入的比较.
  • Ember.js isEqual- 有关此方法的更多信息,请参阅文档来源.它没有对数组进行深入比较.

var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];

if(angular.equals(purple, drank)) {
    document.write('got dat');
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
Run Code Online (Sandbox Code Playgroud)


Ebr*_*owi 62

这是我的版本.它正在使用ES5中引入的新Object.keys功能以及来自+,++的构思/测试:

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 } }));
Run Code Online (Sandbox Code Playgroud)


Joe*_*air 48

如果您使用的是JSON库,则可以将每个对象编码为JSON,然后将生成的字符串进行相等性比较.

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

alert(JSON.encode(obj1)===JSON.encode(obj2));
Run Code Online (Sandbox Code Playgroud)

注意:虽然这个答案在许多情况下都有效,但正如几位人士在评论中所指出的那样,出于各种原因这是有问题的.在几乎所有情况下,您都希望找到更强大的解决方案.

  • 有趣,但在我看来有点棘手.例如,您是否可以100%保证始终以相同的顺序生成对象属性? (88认同)
  • 这是一个很好的问题,并提出另一个问题,即两个具有不同顺序相同属性的对象是否真的相等.我猜,取决于你的意思. (25认同)
  • 请注意,大多数编码器和字符串忽略函数并将非限定数字(如NaN)转换为null. (11认同)
  • 我同意Guido,财产秩序很重要,无法保证.@JoelAnair,我认为如果属性的值相等,那么在不同顺序中具有相同属性的两个对象应该被认为是相等的. (4认同)
  • 这*可以*使用备用JSON字符串,一个对对象键进行一致排序. (4认同)
  • @GuidoGarcía@Juzer`JSON.stringify`不保证关键顺序.[Mozilla docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)说"非数组对象的属性不能保证在任何特定的字符串中order.不要依赖于字符串化中同一对象内的属性排序." (3认同)
  • 请注意,这个答案来自 2008 年,当时 JSON 还不是 JS 规范的一部分。使用现代的 JSON.stringy 和 JSON.parse 函数,这个解决方案工作得很好,因为我们可以通过将预处理函数传递给 stringify (您从未使用过的第二个参数。它非常强大)来确保密钥始终以相同的顺序序列化。 。 (3认同)
  • @JoelAnair,属性没有订单. (2认同)

atm*_*min 33

功能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);
}
Run Code Online (Sandbox Code Playgroud)

编辑:版本2,使用jib的建议和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);
}
Run Code Online (Sandbox Code Playgroud)

  • 您可以用`every`替换`reduce`来简化. (4认同)
  • deepEqual({},[])返回true (3认同)
  • 是的,如果你关心这样的角落情况,丑陋的解决方案是将`:(x === y)`替换为`:( x === y &&(x!= null && y!= null || x.constructor === y.constructor))` (3认同)
  • @nonkertompf 确定他可以:`Object.keys(x).every(key =&gt; deepEqual(x[key], y[key]))`。 (2认同)
  • 比较两个日期时失败 (2认同)
  • 是的,它确实@IsaacPak,您可能评论了错误的答案:P 至少就我而言,该函数与顺序无关。做了一个玩笑测试并适用于所有可能的变化。https://gist.github.com/exapsy/762064fa04ac613f4976ab4c59492f39 (2认同)

Ate*_*ral 20

如果你有一个深拷贝功能方便,你可以使用下面的技巧,仍然使用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);
}
Run Code Online (Sandbox Code Playgroud)

演示:http://jsfiddle.net/CU3vb/3/

理由:

由于属性obj1被逐个复制到克隆,因此将保留它们在克隆中的顺序.当属性obj2被复制到克隆时,由于已经存在的属性obj1将被覆盖,因此将保留克隆中的顺序.

  • 我不认为跨浏览器/引擎保证订单保存. (11认同)
  • @JoLiss谢谢你!但请注意,我从未声称保留代码和编译对象之间的顺序.我声称要保留属性的顺序,其值将被替换为就地.这是我解决方案的关键:使用mixin来覆盖属性值.假设实现通常选择使用某种类型的hashmap,只替换值应保留键的顺序.事实上,我在不同的浏览器中进行过测试. (2认同)

nic*_*ckf 18

你是否试图测试两个物体是否相等?即:他们的财产是平等的?

如果是这种情况,您可能已经注意到这种情况:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"
Run Code Online (Sandbox Code Playgroud)

你可能需要这样做:

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;
}
Run Code Online (Sandbox Code Playgroud)

显然,该函数可以进行相当多的优化,并且可以进行深度检查(处理嵌套对象:),var a = { foo : { fu : "bar" } }但是你明白了.

正如FOR指出的那样,您可能必须根据自己的目的进行调整,例如:不同的类可能有不同的"相等"定义.如果您只是处理普通对象,上面的内容可能就足够了,否则自定义MyClass.equals()函数可能就好了.


Pra*_*iya 18

最简单逻辑的解决方案,用于比较像Object,Array,String,Int ...

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

注意:

  • 你需要替换val1val2你的对象
  • 对于对象,您必须递归(按键)对两个侧对象进行排序

  • 这不适用于具有循环引用的对象 (5认同)
  • 我假设这在许多情况下不起作用,因为对象中键的顺序无关紧要 - 除非`JSON.stringify`按字母顺序进行重新排序?(我找不到[记录](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).) (4认同)

Raf*_*ier 17

在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
Run Code Online (Sandbox Code Playgroud)

另一个返回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;
};
Run Code Online (Sandbox Code Playgroud)


jib*_*jib 13

我使用这个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)));
Run Code Online (Sandbox Code Playgroud)
<div id="div"></div>
Run Code Online (Sandbox Code Playgroud)

在测试中派上用场(大多数测试框架都有一个is功能).例如

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');
Run Code Online (Sandbox Code Playgroud)

如果捕获到差异,则会记录字符串,从而产生差异:

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
Run Code Online (Sandbox Code Playgroud)


Aqe*_*hoo 12

var object1 = {name: "humza" , gender : "male", age: 23}
var object2 = {name: "humza" , gender : "male", age: 23}
var result = Object.keys(object1).every((key) =>  object1[key] === object2[key])
Run Code Online (Sandbox Code Playgroud)

如果 object1 在 object2 上具有相同的值,则结果将为true

  • 如果 object2 具有 object1 不包含的附加键,则此操作将不起作用。 (2认同)

Aid*_*din 12

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

您有4+1 类解决方案


1)使用hacky不完整的快速单线

如果您赶时间并且 99% 的正确性有效,那就太好了。

这方面的例子是,JSON.stringify()建议由PRATIK Bhalodiya,或JSON.encode 由Joel Anair,或者.toString(),或者改变你的对象为一个字符串,然后其他方法使用比较两个字符串===逐个字符。

然而,缺点是字符串中没有对象的全局标准唯一表示。例如{ a: 5, b: 8}{b: 8 and a: 5 }是相等的。

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

我父亲的比喻

说起爸爸,“高大帅气的爸爸”和“高大帅气的爸爸”是同一个人!但是这两个字符串并不相同。

请注意,其实是有一个正确的(标准方式)为了在英语语法,其中的形容词说,它应该是一个“英俊高大的男人,”但你冒着你的能力,如果你一味地假设iOS 8的Safari浏览器的Javascript引擎也守法同样的语法,盲目!#WelcomeToJavascriptNonStandards


2)编写自己的DIY递归函数

如果你正在学习很好。

例子是atmin 的解决方案

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

我只会鼓励人们在练习并且代码不会投入生产时这样做。这是重新发明轮子的唯一理由。

  • 优点:学习机会。
  • 缺点:不可靠。需要时间和担忧。

我父亲的比喻

这就像假设如果我父亲的名字是“John Smith”并且他的生日是“1/1/1970”,那么任何名字是“John Smith”并且出生于“1/1/1970”的人就是我的父亲。

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

2.1 你有限范围的 DIY 比较器

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

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

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

3) 使用equal函数库版本

如果您需要生产级别的质量,并且您无法更改系统的设计,那就太好了。

例子是_.equal lodash,已经在coolaj86 的回答或 Angular 或 Ember的回答中,如Tony Harvey 的回答或 Node 的Rafael Xavier 所述

  • 优点:这是其他人所做的。
  • 缺点:外部依赖,这会花费你额外的内存/CPU/安全问题,甚至是一点点。此外,还可以错过一些边缘的情况下(例如:是否有相同的两个对象ownProperties,但不同的原型家长应该被认为是相同或不)。最后,可能是无意中带辅助的基本设计问题,这一点; 只是说!

我父亲的比喻

这就像花钱请中介找我的亲生父亲,根据他的电话、姓名、地址等。

它会花费更多,而且可能比我自己进行背景调查更准确,但不包括边缘情况,例如我父亲是移民/庇护所并且他的生日未知!


4) 在对象中使用标识符

如果你 [仍然] 可以改变系统的设计(你正在处理的对象)并且你希望你的代码持续很长时间,那就太好了。

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

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

ID 可以是简单的auto_incremental数字,也可以是通过(建议)或一段代码生成的字符串。您需要做的就是确保它始终是唯一的,如果auto_incremental它可以是内置的,或者在 UUID 的情况下,可以检查所有现有值(例如 MySQL 的UNIQUE列属性)或简单地(如果来自库) ) 依赖于提供极低的碰撞可能性。

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

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

我父亲的比喻

就像知道我父亲的社会安全号码是 911-345-9283,所以任何拥有这个 SSN 的人都是我的父亲,任何声称是我父亲的人都必须拥有这个 SSN。


结论

我个人更喜欢解决方案#4(ID)而不是它们的准确性和可靠性。如果不可能,我会选择 #2.1 以实现可预测性,然后使用 #3。如果两者都不可能,#2,最后#1。

  • 当对象的顺序不同时,第一个“hacky”解决方案也根本不起作用。例如。`o1 = { a: '1', b: '2' }` - `o2 = { b: '2', a: '1' }` 比较 `JSON.stringify(o1) === JSON.stringify( o2) = 假` (2认同)

Ala*_*res 10

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

演示在这里


Zac*_*Zac 7

我不知道是否有人发布了与此类似的内容,但这里是我检查对象等式的函数.

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;
}
Run Code Online (Sandbox Code Playgroud)

此外,它是递归的,所以它也可以检查深度相等,如果这就是你所说的.

  • 很明显,正确的相等检查器必须是递归的。我认为这样的递归答案之一应该是正确的答案。接受的答案没有给出代码,也没有帮助 (2认同)

Adr*_*oni 7

ES6:我能完成的最少代码是这样的。它通过字符串化所有排序的表示对象的键值数组来递归地进行深度比较,唯一的限制是没有方法或符号进行比较。

const compareObjects = (a, b) => { 
  let s = (o) => Object.entries(o).sort().map(i => { 
     if(i[1] instanceof Object) i[1] = s(i[1]);
     return i 
  }) 
  return JSON.stringify(s(a)) === JSON.stringify(s(b))
}

console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));
Run Code Online (Sandbox Code Playgroud)

重要提示:此函数在ARRAY 中执行 JSON.stringfy,键已排序,而不是在它自身的对象中:

  1. ["a", ["b", 1]]
  2. ["b", 4]


Pau*_*aul 6

下面是一个简短的实现,它使用但按照JSON.stringify@Jor 建议的方式对键进行排序。

一些测试取自@EbrahimByagowi 的回答

当然,通过使用JSON.stringify,解决方案仅限于 JSON 可序列化类型(字符串、数字、JSON 对象、数组、布尔值、null)。不支持Date、等对象。Function

function objectEquals(obj1, obj2) {
  const JSONstringifyOrder = obj => {
    const keys = {};
    JSON.stringify(obj, (key, value) => {
      keys[key] = null;
      return value;
    });
    return JSON.stringify(obj, Object.keys(keys).sort());
  };
  return JSONstringifyOrder(obj1) === JSONstringifyOrder(obj2);
}

///////////////////////////////////////////////////////////////
/// 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("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}}));
Run Code Online (Sandbox Code Playgroud)


th3*_*erd 5

很多人都没有意识到的这个问题的简单解决方案是对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(''));
}
Run Code Online (Sandbox Code Playgroud)

关于此方法的另一个有用的事情是您可以通过将"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;
});
Run Code Online (Sandbox Code Playgroud)

  • `areEqual({a:'b'},{b:'a'})`得到`true`然后呢? (11认同)

Ego*_*huk 5

只是想利用一些 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;

        }
    )
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*sma 5

你可以使用_.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
Run Code Online (Sandbox Code Playgroud)

请参阅此处的官方文档:http://underscorejs.org/#isEqual


xam*_*mir 5

假定对象中属性的顺序未更改。

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));
Run Code Online (Sandbox Code Playgroud)

  • 不同顺序的属性是什么???不,不是一个好方法 (3认同)

Vae*_*lin 5

对于那些使用NodeJS的用户,可以isDeepStrictEqual在本机Util库上调用一种方便的方法来实现此目的。

const util = require('util');

const foo = {
  hey: "ho",
  lets: "go"
}

const bar = {
  hey: "ho",
  lets: "go"
}

foo == bar // false
util.isDeepStrictEqual(foo, bar) // true
Run Code Online (Sandbox Code Playgroud)

https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2


归档时间:

查看次数:

450521 次

最近记录:

5 年,11 月 前