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

严格等号操作符将告诉您两个对象类型是否相等。但是,有没有办法判断两个对象是否相等,Java中的很像哈希码值?

Stack Overflow问题JavaScript中是否有任何类型的hashCode函数?与此问题类似,但需要更学术的答案。上面的场景演示了为什么有必要拥有一个,我想知道是否有等效溶液

788212 次浏览

取决于你所说的相等是什么意思。因此,作为类的开发人员,由你来定义它们的相等性。

有时会使用一种情况,如果两个实例指向内存中的相同位置,则它们被认为“相等”,但这并不总是您想要的。例如,如果我有一个Person类,我可能希望将两个Person对象视为“相等”,如果它们具有相同的姓氏、名字和社会安全号码(即使它们指向内存中的不同位置)。

另一方面,如果两个对象的每个成员的值相同,我们不能简单地说两个对象是相等的,因为有时你不希望这样。换句话说,对于每个类,由类开发人员定义哪些成员组成对象的“身份”并开发适当的等号操作符(无论是通过重载==运算符还是Equals方法)。

一种方法是,如果两个对象具有相同的哈希值,则表示它们相等。然而,你不得不想知道每个实例的哈希是如何计算的。回到上面的Person示例,如果哈希是通过查看名字、姓氏和社会安全号码字段的值来计算的,我们可以使用这个系统。最重要的是,我们然后依赖哈希方法的质量(这本身就是一个巨大的话题,但足以说明并非所有哈希都是平等的,糟糕的哈希方法会导致更多碰撞,在这种情况下会返回错误的匹配)。

你想测试两个对象是否相等?即:它们的属性相等吗?

如果是这样的话,你可能已经注意到了这种情况:

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指出的,你可能必须根据自己的目的调整它,例如:不同的类可能对“等于”有不同的定义。如果你只是使用普通对象,以上可能就足够了,否则自定义MyClass.equals()函数可能是要走的路。

如果您使用的是JSON库,您可以将每个对象编码为JSON,然后比较结果字符串是否相等。

var obj1={test:"value"};var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));

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

简短的回答

简单的答案是:不,没有通用的方法来确定一个对象是否等于你所指的另一个对象。例外是当你严格地认为一个对象是无类型的时候。

冗长的答案

这个概念是一个Equals方法的概念,它比较一个对象的两个不同实例以指示它们在值级别上是否相等。然而,由特定类型来定义应该如何实现Equals方法。对具有原始值的属性进行迭代比较可能不够:对象可能包含与相等无关的属性。例如,

 function MyClass(a, b){var c;this.getCLazy = function() {if (c === undefined) c = a * b // imagine * is really expensivereturn c;}}

在上述情况下,c对于确定MyClass的任何两个实例是否相等并不重要,只有ab很重要。在某些情况下,c可能在实例之间有所不同,但在比较过程中并不重要。

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

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

一个对象可能会引用一个要作为事件处理程序调用的方法,而这可能不会被视为其“值状态”的一部分。而另一个对象很可能被分配了一个执行重要计算的函数,从而使这个实例与其他实例不同,仅仅因为它引用了不同的函数。

如果一个对象的一个现有原型方法被另一个函数覆盖,该对象怎么办?它是否仍然被认为等于它在其他方面相同的另一个实例?这个问题只能在每种类型的每个特定情况下回答。

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

如果你需要测试两个对象是否相等,那么你需要定义equals的含义。可能是两个对象中的所有数据成员都匹配,也可能是内存位置必须匹配(意味着两个变量引用内存中的同一个对象),或者可能是每个对象中只有一个数据成员必须匹配。

最近我开发了一个对象,它的构造函数在每次创建实例时都会创建一个新的id(从1开始并递增1)。这个对象有一个is平等函数,它将该id值与另一个对象的id值进行比较,如果它们匹配,则返回true。

在这种情况下,我将“相等”定义为id值匹配。鉴于每个实例都有一个唯一的id,这可以用来强制执行匹配对象也占用相同内存位置的想法。尽管这不是必要的。

JavaScript for Object中的默认等号操作符在引用内存中的同一位置时产生true。

var x = {};var y = {};var z = x;
x === y; // => falsex === 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); // => falsekingOfSpades.equals(new Card(13, "S")); // => true

我需要模拟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' });

需要一个比发布的更通用的对象比较函数,我制作了以下内容。批评赞赏…

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

为什么要重新发明轮子?试试Lodash。它有许多必备的功能,例如是否相等

_.isEqual(object, other);

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

注意:以前这个答案推荐Underscore.js,但是豆沙在修复错误和一致性解决问题方面做得更好。

如果两个对象的所有属性和所有嵌套对象和数组的递归值都相同,则将它们视为相等是有用的。我还认为以下两个对象相等:

var a = {p1: 1};var b = {p1: 1, p2: 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):单元测试试车手在这里

如果你有一个深度复制函数,你可以使用以下技巧来仍然使用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中已经存在的属性将被简单地覆盖,它们在克隆中的顺序将被保留。

我写了一个运行在Node.js和浏览器上的小库,名为compare.js.它提供了通常的比较运算符,例如 ==, ! =, >, >=, &,<=和JavaScript所有数据类型的标识。

例如,您可以使用

cmp.eq(obj1, obj2);

这将检查是否相等(使用深度相等方法)。否则,如果你这样做

cmp.id(obj1, obj2);

它将通过引用进行比较,因此检查身份。您还可以在对象上使用<和>,这意味着子集和超集。

compare.js涵盖了近700个单元测试,因此希望它不会有太多的错误;-)。

您可以在https://github.com/goloroden/compare.js上免费找到它,它是在MIT许可下开源的。

判断两个对象是否相似的一个快速“技巧”是使用它们的toString()方法。如果您要检查对象A和B,请确保A和B具有有意义的toString()方法并检查它们返回的字符串是否相同。

这不是灵丹妙药,但有时在正确的情况下可能有用。

function isEqual(obj1, obj2){type1 = typeof(obj1);type2 = typeof(obj2);if(type1===type2){switch (type1){case "object": return JSON.stringify(obj1)===JSON.stringify(obj2);case "function": return eval(obj1).toString()===eval(obj2).toString();default: return obj1==obj2;}}return false;}//have not tried but should work.

这是我的版本。它使用了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 enoughif (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 valueOfif (x instanceof Date) { return false; }
// if they are strictly equal, they both need to be object at leastif (!(x instanceof Object)) { return false; }if (!(y instanceof Object)) { return false; }
// recursive object equality checkvar 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 postassert.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 functionalityvar 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 } }));

这里有一个非常干净的脚本编写版本,你可以这样做:

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]' ortypeOf.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"})

以下几种方案,有的是性能、功能、样式存在问题……考虑不够周全,且各有不同的失效情况。我曾尝试用自己的方案来解决这个问题,希望大家给我反馈:

//Returns the object's class, Array, Date, RegExp, Object are of interest to usvar getClass = function(val) {return Object.prototype.toString.call(val).match(/^\[object\s(.*)\]$/)[1];};
//Defines the type of the value, extended typeofvar whatis = function(val) {
if (val === undefined)return 'undefined';if (val === null)return 'null';
var type = typeof val;
if (type === 'object')type = getClass(val).toLowerCase();
if (type === 'number') {if (val.toString().indexOf('.') > 0)return 'float';elsereturn 'integer';}
return type;};
var compareObjects = function(a, b) {if (a === b)return true;for (var i in a) {if (b.hasOwnProperty(i)) {if (!equal(a[i],b[i])) return false;} else {return false;}}
for (var i in b) {if (!a.hasOwnProperty(i)) {return false;}}return true;};
var compareArrays = function(a, b) {if (a === b)return true;if (a.length !== b.length)return false;for (var i = 0; i < a.length; i++){if(!equal(a[i], b[i])) return false;};return true;};
var _equal = {};_equal.array = compareArrays;_equal.object = compareObjects;_equal.date = function(a, b) {return a.getTime() === b.getTime();};_equal.regexp = function(a, b) {return a.toString() === b.toString();};//  uncoment to support function as string compare//  _equal.fucntion =  _equal.regexp;


/** Are two values equal, deep compare for objects and arrays.* @param a {any}* @param b {any}* @return {boolean} Are equal?*/var equal = function(a, b) {if (a !== b) {var atype = whatis(a), btype = whatis(b);
if (atype === btype)return _equal.hasOwnProperty(atype) ? _equal[atype](a, b) : a==b;
return false;}
return true;};

我对这个函数做了以下假设:

  1. 您可以控制要比较的对象,并且只有原始值(即没有嵌套对象、函数等)。
  2. 您的浏览器支持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 elementsif( 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 keysfor( i = 0; i < keys1.length; i++ ) {if( keys1[i] != keys2[i] ) {return false;}}
// Test 3: Valuesfor( i = 0; i < keys1.length; i++ ) {key = keys1[i];if( object1[key] != object2[key] ) {return false;}}
return true;}

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

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

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>

为了比较简单键/值对对象实例的键,我使用:

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个属性…

这是对上述所有内容的补充,而不是替换。如果您需要快速浅比较对象而无需检查额外的递归情况。这里是一个镜头。

这比较为:1)自己属性的数量相等,2)键名称相等,3)如果bCompareValue==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;}

对象相等性检查:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

上述测试也适用于对象数组,在这种情况下使用http://www.w3schools.com/jsref/jsref_sort.asp中记录的排序函数

对于具有平面JSON模式的小数组可能就足够了。

如果要比较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 equalconsole.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

如果两个对象不同,则返回MongoDB兼容的{$rename:{...}, $unset:{...}, $set:{...}}类型对象。

这是一个检查对象“值相等”的非常基本的方法。

var john = {occupation: "Web Developer",age: 25};
var bobby = {occupation: "Web Developer",age: 25};
function isEquivalent(a, b) {// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different, objects are not equivalent
if (aProps.length != bProps.length) {return false;}
for (var i = 0; i < aProps.length; i++) {var propName = aProps[i];
// If values of same property are not equal, objects are not equivalentif (a[propName] !== b[propName]) {return false;}}
// If we made it this far, objects are considered equivalentreturn true;}
// Outputs: trueconsole.log(isEquivalent(john, bobby));

演示-JSFiddle

如你所见,要检查对象的“值相等”,我们基本上必须迭代对象中的每个属性以查看它们是否相等。虽然这个简单的实现适用于我们的示例,但有很多情况它无法处理。例如:

  • 如果其中一个属性值本身就是一个对象怎么办?
  • 如果属性值之一是NaN(中的唯一值不等于自身的JavaScript?)
  • 如果a有一个值未定义的属性,而b没有此属性(因此计算结果为未定义?)

对于检查对象“值相等”的健壮方法,最好依赖于经过良好测试的库,该库涵盖了下划线等各种边缘情况。

var john = {occupation: "Web Developer",age: 25};
var bobby = {occupation: "Web Developer",age: 25};
// Outputs: trueconsole.log(_.isEqual(john, bobby));

演示-JSFiddle

在Node.js中,您可以使用其原生require("assert").deepStrictEqual。更多信息:http://nodejs.org/api/assert.html

例如:

var assert = require("assert");assert.deepStrictEqual({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;};

当然,当我们在它的时候,我会把我自己的轮子改造(我为使用的辐条和材料的数量感到自豪):

////////////////////////////////////////////////////////////////////////////////
var equals = function ( objectA, objectB ) {var result = false,keysA,keysB;
// Check if they are pointing at the same variable. If they are, no need to test further.if ( objectA === objectB ) {return true;}
// Check if they are the same type. If they are not, no need to test further.if ( typeof objectA !== typeof objectB ) {return false;}
// Check what kind of variables they are to see what sort of comparison we should make.if ( typeof objectA === "object" ) {// Check if they have the same constructor, so that we are comparing apples with apples.if ( objectA.constructor === objectA.constructor ) {// If we are working with Arrays...if ( objectA instanceof Array ) {// Check the arrays are the same length. If not, they cannot be the same.if ( objectA.length === objectB.length ) {// Compare each element. They must be identical. If not, the comparison stops immediately and returns false.return objectA.every(function ( element, i ) {return equals( element, objectB[ i ] );});}// They are not the same length, and so are not identical.else {return false;}}// If we are working with RegExps...else if ( objectA instanceof RegExp ) {// Return the results of a string comparison of the expression.return ( objectA.toString() === objectB.toString() );}// Else we are working with other types of objects...else {// Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.keysA = Object.keys( objectA );
keysB = Object.keys( objectB );
// Check the key arrays are the same length. If not, they cannot be the same.if ( keysA.length === keysB.length ) {// Compare each property. They must be identical. If not, the comparison stops immediately and returns false.return keysA.every(function ( element ) {return equals( objectA[ element ], objectB[ element ] );});}// They do not have the same number of keys, and so are not identical.else {return false;}}}// They don't have the same constructor.else {return false;}}// If they are both functions, let us do a string comparison.else if ( typeof objectA === "function" ) {return ( objectA.toString() === objectB.toString() );}// If a simple variable type, compare directly without coercion.else {return ( objectA === objectB );}
// Return a default if nothing has already been returned.return result;};
////////////////////////////////////////////////////////////////////////////////

它尽可能快地返回false,但当然对于差异嵌套很深的大型对象,它可能不太有效。在我自己的场景中,良好处理嵌套数组很重要。

希望它能帮助需要这种“轮子”的人。

编辑:这个方法是相当有缺陷的,并且充满了自己的问题。我不推荐它,并会感谢一些否决!这是有问题的,因为1)有些东西不能比较(即函数),因为它们不能被序列化,2)这不是一个非常快速的比较方法,3)它有排序问题,4)如果没有正确实现,它可能会有碰撞问题/误报,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(''));}

此方法的另一个有用之处是,您可以通过将“替换”函数传递给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;});

我知道这有点陈旧,但我想添加一个我为这个问题想出的解决方案。我有一个对象,我想知道它的数据什么时候改变了。“类似于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。

是啊另一个答案…

Object.prototype.equals = function (object) {if (this.constructor !== object.constructor) return false;if (Object.keys(this).length !== Object.keys(object).length) return false;var obk;for (obk in object) {if (this[obk] !== object[obk])return false;}return true;}
var aaa = JSON.parse('{"name":"mike","tel":"1324356584"}');var bbb = JSON.parse('{"tel":"1324356584","name":"mike"}');var ccc = JSON.parse('{"name":"mike","tel":"584"}');var ddd = JSON.parse('{"name":"mike","tel":"1324356584", "work":"nope"}');
$("#ab").text(aaa.equals(bbb));$("#ba").text(bbb.equals(aaa));$("#bc").text(bbb.equals(ccc));$("#ad").text(aaa.equals(ddd));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>aaa equals bbb? <span id="ab"></span> <br/>bbb equals aaa? <span id="ba"></span> <br/>bbb equals ccc? <span id="bc"></span> <br/>aaa equals ddd? <span id="ad"></span>

从我的个人库中取出,我经常使用它来工作。以下函数是一个宽松的递归深度相等,不检查

  • 阶级平等
  • 继承的价值观
  • 价值观严格平等

我主要用它来检查我是否对各种API实现得到相等的回复。可能发生实现差异(如字符串与数字)和其他空值的地方。

它的实现非常简单和简短(如果所有注释都被剥离)

/** Recursively check if both objects are equal in value****** This function is designed to use multiple methods from most probable*** (and in most cases) valid, to the more regid and complex method.****** One of the main principles behind the various check is that while*** some of the simpler checks such as == or JSON may cause false negatives,*** they do not cause false positives. As such they can be safely run first.****** # !Important Note:*** as this function is designed for simplified deep equal checks it is not designed*** for the following****** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)*** - Inherited values, this actually ignores them*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)*** - Performance across all cases. This is designed for high performance on the***   most probable cases of == / JSON equality. Consider bench testing, if you have***   more 'complex' requirments****** @param  objA : First object to compare*** @param  objB : 2nd object to compare*** @param  .... : Any other objects to compare****** @returns true if all equals, or false if invalid****** @license Copyright by eugene@picoded.com, 2012.***          Licensed under the MIT license: http://opensource.org/licenses/MIT**/function simpleRecusiveDeepEqual(objA, objB) {// Multiple comparision check//--------------------------------------------var args = Array.prototype.slice.call(arguments);if(args.length > 2) {for(var a=1; a<args.length; ++a) {if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {return false;}}return true;} else if(args.length < 2) {throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";}	
// basic equality check,//--------------------------------------------// if this succed the 2 basic values is equal,// such as numbers and string.//// or its actually the same object pointer. Bam//// Note that if string and number strictly equal is required// change the equality from ==, to ===//if(objA == objB) {return true;}	
// If a value is a bsic type, and failed above. This failsvar basicTypes = ["boolean", "number", "string"];if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {return false;}	
// JSON equality check,//--------------------------------------------// this can fail, if the JSON stringify the objects in the wrong order// for example the following may fail, due to different string order://// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )//if(JSON.stringify(objA) == JSON.stringify(objB)) {return true;}	
// Array equality check//--------------------------------------------// This is performed prior to iteration check,// Without this check the following would have been considered valid//// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );//// Note that u may remove this segment if this is what is intended//if( Array.isArray(objA) ) {//objA is array, objB is not an arrayif( !Array.isArray(objB) ) {return false;}} else if( Array.isArray(objB) ) {//objA is not array, objB is an arrayreturn false;}	
// Nested values iteration//--------------------------------------------// Scan and iterate all the nested values, and check for non equal values recusively//// Note that this does not check against null equality, remove the various "!= null"// if this is required	
var i; //reuse var to iterate	
// Check objA values against objBfor (i in objA) {//Protect against inherited propertiesif(objA.hasOwnProperty(i)) {if(objB.hasOwnProperty(i)) {// Check if deep equal is validif(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {return false;}} else if(objA[i] != null) {//ignore null values in objA, that objB does not have//else failsreturn false;}}}	
// Check if objB has additional values, that objA do not, fail if sofor (i in objB) {if(objB.hasOwnProperty(i)) {if(objB[i] != null && !objA.hasOwnProperty(i)) {//ignore null values in objB, that objA does not have//else failsreturn false;}}}	
// End of all checks//--------------------------------------------// By reaching here, all iteration scans have been done.// and should have returned false if it failedreturn true;}
// Sanity checking of simpleRecusiveDeepEqual(function() {if(// Basic checks!simpleRecusiveDeepEqual({}, {}) ||!simpleRecusiveDeepEqual([], []) ||!simpleRecusiveDeepEqual(['a'], ['a']) ||// Not strict checks!simpleRecusiveDeepEqual("1", 1) ||// Multiple objects check!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||// Ensure distinction between array and object (the following should fail)simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||// Null strict checkssimpleRecusiveDeepEqual( 0, null ) ||simpleRecusiveDeepEqual( "", null ) ||// Last "false" exists to make the various check above easy to comment in/outfalse) {alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");} else {//added this last line, for SO snippet alert on successalert("simpleRecusiveDeepEqual: Passed all checks, Yays!");}})();

我不是Javascript专家,但这里有一个简单的尝试来解决它。我检查三件事:

  1. 它是一个object,而且它不是null,因为typeof nullobject
  2. 如果两个对象的属性计数相同?如果不是,它们就不相等。
  3. 遍历一个对象的属性并检查对应的属性在第二个对象中是否具有相同的值。

function deepEqual (first, second) {// Not equal if either is not an object or is null.if (!isObject(first) || !isObject(second) ) return false;
// If properties count is differentif (keys(first).length != keys(second).length) return false;
// Return false if any property value is different.for(prop in first){if (first[prop] != second[prop]) return false;}return true;}
// Checks if argument is an object and is not nullfunction isObject(obj) {return (typeof obj === "object" && obj != null);}
// returns arrays of object keysfunction keys (obj) {result = [];for(var key in obj){result.push(key);}return result;}
// Some test codeobj1 = {name: 'Singh',age: 20}
obj2 = {age: 20,name: 'Singh'}
obj3 = {name: 'Kaur',age: 19}
console.log(deepEqual(obj1, obj2));console.log(deepEqual(obj1, obj3));

以下是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;}}

演示在这里可用

短功能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箭头函数:

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

我使用这个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>

Comes in handy in tests (most test frameworks have an is function). E.g.

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

如果捕获到差异,则会记录字符串,从而使差异成为可识别的:

x must match ygot      {"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}}.

我有一个更短的函数,它将深入所有子对象或数组。它与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 lengthfor (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 differenthat : {cap : ['something', null ],helmet : [ 'triple eight', 'pro-tec' ]}},{beers : [ 'budweiser', 'busch' ],wines : [ 'barefoot', 'yellow tail' ]}];
console.log(deep_equal(obj1, obj2)); // trueconsole.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // falseconsole.log(deep_equal([], [])); // trueconsole.log(deep_equal({}, {})); // trueconsole.log(deep_equal([], {})); // false

如果你想添加对FunctionDateRegExp的支持,你可以在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();}

我也遇到了同样的问题,并决定编写自己的解决方案。但是因为我也想比较数组和对象,反之亦然,所以我制定了一个通用的解决方案。我决定将函数添加到原型中,但可以轻松地将它们重写为独立函数。下面是代码:

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函数本身和查找数组/对象中属性的数字索引的函数。只需要查找函数,因为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不同。

我的版本,其中包括区别在哪里找到的链,以及区别是什么。

function DeepObjectCompare(O1, O2){try {DOC_Val(O1, O2, ['O1->O2', O1, O2]);return DOC_Val(O2, O1, ['O2->O1', O1, O2]);} catch(e) {console.log(e.Chain);throw(e);}}function DOC_Error(Reason, Chain, Val1, Val2){this.Reason=Reason;this.Chain=Chain;this.Val1=Val1;this.Val2=Val2;}
function DOC_Val(Val1, Val2, Chain){function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }
if(typeof(Val1)!==typeof(Val2))return DoThrow('Type Mismatch');if(Val1===null || Val1===undefined)return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;if(Val1.constructor!==Val2.constructor)return DoThrow('Constructor mismatch');switch(typeof(Val1)){case 'object':for(var m in Val1){if(!Val1.hasOwnProperty(m))continue;var CurChain=Chain.concat([m]);if(!Val2.hasOwnProperty(m))return DoThrow('Val2 missing property', CurChain);DOC_Val(Val1[m], Val2[m], CurChain);}return true;case 'number':if(Number.isNaN(Val1))return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;case 'string':case 'boolean':return Val1!==Val2 ? DoThrow('Value mismatch') : true;case 'function':if(Val1.prototype!==Val2.prototype)return DoThrow('Prototype mismatch');if(Val1!==Val2)return DoThrow('Function mismatch');return true;default:return DoThrow('Val1 is unknown type');}}

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

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

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

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

这是一个通用的相等检查器函数,它接收元素数组作为输入并将它们相互比较。适用于所有类型的元素。

const isEqual = function(inputs = []) {// Checks an element if js object.const isObject = function(data) {return Object.prototype.toString.call(data) === '[object Object]';};// Sorts given object by its keys.const sortObjectByKey = function(obj) {const self = this;if (!obj) return {};return Object.keys(obj).sort().reduce((initialVal, item) => {initialVal[item] = !Array.isArray(obj[item]) &&typeof obj[item] === 'object'? self.objectByKey(obj[item]): obj[item];return initialVal;}, {});};
// Checks equality of all elements in the input against each other. Returns true | falsereturn (inputs.map(input =>typeof input == 'undefined'? '': isObject(input)? JSON.stringify(sortObjectByKey(input)): JSON.stringify(input)).reduce((prevValue, input) =>prevValue === '' || prevValue === input ? input : false,'') !== false);};
// Tests (Made with Jest test framework.)test('String equality check', () => {expect(isEqual(['murat'])).toEqual(true);expect(isEqual(['murat', 'john', 'doe'])).toEqual(false);expect(isEqual(['murat', 'murat', 'murat'])).toEqual(true);});
test('Float equality check', () => {expect(isEqual([7.89, 3.45])).toEqual(false);expect(isEqual([7, 7.50])).toEqual(false);expect(isEqual([7.50, 7.50])).toEqual(true);expect(isEqual([7, 7])).toEqual(true);expect(isEqual([0.34, 0.33])).toEqual(false);expect(isEqual([0.33, 0.33])).toEqual(true);});
test('Array equality check', () => {expect(isEqual([[1, 2, 3], [1, 2, 3]])).toEqual(true);expect(isEqual([[1, 3], [1, 2, 3]])).toEqual(false);expect(isEqual([['murat', 18], ['murat', 18]])).toEqual(true);});
test('Object equality check', () => {let obj1 = {name: 'murat',age: 18};let obj2 = {name: 'murat',age: 18};let obj3 = {age: 18,name: 'murat'};let obj4 = {name: 'murat',age: 18,occupation: 'nothing'};expect(isEqual([obj1, obj2])).toEqual(true);expect(isEqual([obj1, obj2, obj3])).toEqual(true);expect(isEqual([obj1, obj2, obj3, obj4])).toEqual(false);});
test('Weird equality checks', () => {expect(isEqual(['', {}])).toEqual(false);expect(isEqual([0, '0'])).toEqual(false);});

还有一个要点

我实现了一个方法,它接受两个json并使用递归检查它们的键是否具有相同的值。我用另一个问题来解决这个问题。

const arraysEqual = (a, b) => {if (a === b)return true;if (a === null || b === null)return false;if (a.length !== b.length)return false;
// If you don't care about the order of the elements inside// the array, you should sort both arrays here.
for (let i = 0; i < a.length; ++i) {if (a[i] !== b[i])return false;}return true;};
const jsonsEqual = (a, b) => {
if(typeof a !== 'object' || typeof b !== 'object')return false;
if (Object.keys(a).length === Object.keys(b).length) { // if items have the same sizelet response = true;
for (let key in a) {if (!b[key]) // if not keyresponse = false;if (typeof a[key] !== typeof b[key]) // if typeof doesn't equalsresponse = false;else {if (Array.isArray(a[key])) // if arrayresponse = arraysEqual(a[key], b[key]);else if (typeof a[key] === 'object') // if another jsonresponse = jsonsEqual(a[key], b[key]);else if (a[key] !== b[key])  // not equalsresponse = false;}if (!response) // return if one item isn't equalreturn false;}} elsereturn false;
return true;};
const json1 = {a: 'a',b: 'asd',c: ['1',2,2.5,'3',{d: 'asd',e: [1.6,{f: 'asdasd',g: '123'}]}],h: 1,i: 1.2,};
const json2 = {a: 'nops',b: 'asd'};
const json3 = {a: 'h',b: '484',c: [3,4.5,'2ss',{e: [{f: 'asdasd',g: '123'}]}],h: 1,i: 1.2,};
const result = jsonsEqual(json1,json2);//const result = jsonsEqual(json1,json3);//const result = jsonsEqual(json1,json1);
if(result) // is equal$('#result').text("Jsons are the same")else$('#result').text("Jsons aren't equals")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>

简单逻辑解决方案,用于比较对象、数组、字符串、Int…等所有内容

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

备注:

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

这是stringify技巧的一个版本,它的类型较少,并且在很多情况下适用于琐碎的JSON数据比较。

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }

这里有很多好主意!这是我的深度平等版本。我把它发布在github上并围绕它写了一些测试。很难涵盖所有可能的情况,有时也没有必要这样做。

我介绍了NaN !== NaN以及循环依赖项。

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

您可以从underscore.js库中使用_.isEqual(obj1, obj2)

下面是一个例子:

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

假设对象中属性的顺序没有改变。

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

取决于。如果对象中键的顺序不重要,并且我不需要知道所述对象的原型。使用总是做这项工作。

const object = {};JSON.stringify(object) === "{}" will pass but {} === "{}" will not

我看到意大利面条代码的答案。不使用任何第三方库,这很容易。

首先按键对两个对象进行排序,它们的键名。

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)

     let user1 = {name: "John",address: {line1: "55 Green Park Road",line2: {a:[1,2,3]}},email:null}    
let user2 = {name: "John",address: {line1: "55 Green Park Road",line2: {a:[1,2,3]}},email:null}    
// Method 1    
function isEqual(a, b) {return JSON.stringify(a) === JSON.stringify(b);}    
// Method 2    
function isEqual(a, b) {// checking type of a And bif(typeof a !== 'object' || typeof b !== 'object') {return false;}      
// Both are NULLif(!a && !b ) {return true;} else if(!a || !b) {return false;}      
let keysA = Object.keys(a);let keysB = Object.keys(b);if(keysA.length !== keysB.length) {return false;}for(let key in a) {if(!(key in b)) {return false;}        
if(typeof a[key] === 'object') {if(!isEqual(a[key], b[key])){return false;}} else {if(a[key] !== b[key]) {return false;}}}      
return true;}


console.log(isEqual(user1,user2));

对于那些使用Node的人来说,在本机util库中有一个名为isDeepStrictEqual的方便方法可以实现这一点。

const util = require('util');
const obj1 = {foo: "bar",baz: [1, 2]};
const obj2 = {foo: "bar",baz: [1, 2]};

obj1 == obj2 // falseutil.isDeepStrictEqual(obj1, obj2) // true

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

function isDeepEqual(obj1, obj2, testPrototypes = false) {if (obj1 === obj2) {return true}
if (typeof obj1 === "function" && typeof obj2 === "function") {return obj1.toString() === obj2.toString()}
if (obj1 instanceof Date && obj2 instanceof Date) {return obj1.getTime() === obj2.getTime()}
if (Object.prototype.toString.call(obj1) !==Object.prototype.toString.call(obj2) ||typeof obj1 !== "object") {return false}
const prototypesAreEqual = testPrototypes? isDeepEqual(Object.getPrototypeOf(obj1),Object.getPrototypeOf(obj2),true): true
const obj1Props = Object.getOwnPropertyNames(obj1)const obj2Props = Object.getOwnPropertyNames(obj2)
return (obj1Props.length === obj2Props.length &&prototypesAreEqual &&obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop])))}
console.log(isDeepEqual({key: 'one'}, {key: 'first'}))console.log(isDeepEqual({key: 'one'}, {key: 'one'}))

这是一个经典的javascript问题!我创建了一个方法来检查深度对象相等性,其特点是能够从比较中选择要忽略的属性。参数是要比较的两个对象,加上一个可选的字符串化属性忽略相对路径数组。

function isObjectEqual( o1, o2, ignorePropsArr=[]) {// Deep Clone objectslet _obj1 = JSON.parse(JSON.stringify(o1)),_obj2 = JSON.parse(JSON.stringify(o2));// Remove props to ignoreignorePropsArr.map( p => {eval('_obj1.'+p+' = _obj2.'+p+' = "IGNORED"');});// compare as stringslet s1 = JSON.stringify(_obj1),s2 = JSON.stringify(_obj2);// return [s1==s2,s1,s2];return s1==s2;}
// Objects 0 and 1 are exact equalsobj0 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}obj1 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}obj2 = { price: 66544.12, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}obj3 = { price: 66544.13, RSIs: [0.000432334, 0.00046531], candles: {A: 541, B: 321, C: 4322}}obj4 = { price: 66544.14, RSIs: [0.000432334, 0.00046530], candles: {A: 543, B: 321, C: 4322}}
isObjectEqual(obj0,obj1) // trueisObjectEqual(obj0,obj2) // falseisObjectEqual(obj0,obj2,['price']) // trueisObjectEqual(obj0,obj3,['price']) // falseisObjectEqual(obj0,obj3,['price','candles.A']) // trueisObjectEqual(obj0,obj4,['price','RSIs[1]'])   // true

这是一个简单的Javascript函数,用于比较两个具有简单键值对的对象。该函数将返回一个字符串数组,其中每个字符串都是两个对象之间不等式的路径。

function compare(a,b) {var paths = [];[...new Set(Object.keys(a).concat(Object.keys(b)))].forEach(key=>{if(typeof a[key] === 'object' && typeof b[key] === 'object') {var results = compare(a[key], b[key]);if(JSON.stringify(results)!=='[]') {paths.push(...results.map(result=>key.concat("=>"+result)));}}else if (a[key]!==b[key]) {paths.push(key);}})return paths;}

如果你只想比较两个对象而不知道不等式的路径,你可以这样做:

if(JSON.stringify(compare(object1, object2))==='[]') {// the two objects are equal} else {// the two objects are not equal}

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

重要事项:此函数在阵列中执行JSON.stringfy,键排序,不是在对象it self中:

  1. ["a",["b",1]]
  2. ["b",4]
const one={name:'mohit' , age:30};//const two ={name:'mohit',age:30};const two ={age:30,name:'mohit'};
function isEquivalent(a, b) {// Create arrays of property namesvar aProps = Object.getOwnPropertyNames(a);var bProps = Object.getOwnPropertyNames(b);


// If number of properties is different,// objects are not equivalentif (aProps.length != bProps.length) {return false;}
for (var i = 0; i < aProps.length; i++) {var propName = aProps[i];
// If values of same property are not equal,// objects are not equivalentif (a[propName] !== b[propName]) {return false;}}
// If we made it this far, objects// are considered equivalentreturn true;}
console.log(isEquivalent(one,two))
  1. 对对象进行排序(字典)
  2. 比较JSON字符串

    function areTwoDictsEqual(dictA, dictB) {function sortDict(dict) {var keys = Object.keys(dict);keys.sort();
    var newDict = {};for (var i=0; i<keys.length; i++) {var key = keys[i];var value = dict[key];newDict[key] = value;}return newDict;}
    return JSON.stringify(sortDict(dictA)) == JSON.stringify(sortDict(dictB));}

经过这么多的搜索,我找到了以下工作解决方案

function isEquivalent(a, b) {// Create arrays of property namesvar aProps = Object.getOwnPropertyNames(a);var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different, objects are not equivalentif (aProps.length != bProps.length) {return false;}
for (var i = 0; i < aProps.length; i++) {var propName = aProps[i];
// If values of same property are not equal, objects are not equivalentif (a[propName] !== b[propName]) {return false;}}
// If we made it this far, objects are considered equivalentreturn true; }

更多信息:JavaScript中的对象平等

虽然这个问题已经有很多答案了。我的尝试只是提供另一种实现方法:

const primitveDataTypes = ['number', 'boolean', 'string', 'undefined'];const isDateOrRegExp = (value) => value instanceof Date || value instanceof RegExp;const compare = (first, second) => {let agg = true;if(typeof first === typeof second && primitveDataTypes.indexOf(typeof first) !== -1 && first !== second){agg =  false;}// adding support for Date and RegExp.else if(isDateOrRegExp(first) || isDateOrRegExp(second)){if(first.toString() !== second.toString()){agg = false;}}else {if(Array.isArray(first) && Array.isArray(second)){if(first.length === second.length){for(let i = 0; i < first.length; i++){if(typeof first[i] === 'object' && typeof second[i] === 'object'){agg = compare(first[i], second[i]);}else if(first[i] !== second[i]){agg = false;}}} else {agg = false;}} else {const firstKeys = Object.keys(first);const secondKeys = Object.keys(second);if(firstKeys.length !== secondKeys.length){agg = false;}for(let j = 0 ; j < firstKeys.length; j++){if(firstKeys[j] !== secondKeys[j]){agg = false;}if(first[firstKeys[j]] && second[secondKeys[j]] && typeof first[firstKeys[j]] === 'object' && typeof second[secondKeys[j]] === 'object'){agg = compare(first[firstKeys[j]], second[secondKeys[j]]);}else if(first[firstKeys[j]] !== second[secondKeys[j]]){agg = false;}}}}return agg;}

console.log('result', compare({a: 1, b: { c: [4, {d:5}, {e:6}]}, r: null}, {a: 1, b: { c: [4, {d:5}, {e:6}]}, r: 'ffd'}));  //returns false.

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])

如果对象1在对象2上具有相同的值,则结果将为真正

如何确定部分对象(Parous)等于打字稿中的原始对象(T)。

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

P. S.最初我打算创建一个带有答案的新问题。但这样的问题已经存在并标记为重复。

我写这个方法只是为了确保数组和对象都以清晰的方式进行比较。

这也应该起作用!:)

public class Objects {/*** Checks whether a value is of type Object* @param value the value*/public static isObject = (value: any): boolean => {return value === Object(value) && Object.prototype.toString.call(value) !== '[object Array]'}
/*** Checks whether a value is of type Array* @param value the value*/public static isArray = (value: any): boolean => {return Object.prototype.toString.call(value) === '[object Array]' && !Objects.isObject(value)}
/*** Check whether two values are equal*/public static isEqual = (objectA: any, objectB: any) => {// Objectsif (Objects.isObject(objectA) && !Objects.isObject(objectB)) {return false}else if (!Objects.isObject(objectA) && Objects.isObject(objectB)) {return false}// Arrayselse if (Objects.isArray(objectA) && !Objects.isArray(objectB)) {return false}else if (!Objects.isArray(objectA) && Objects.isArray(objectB)) {return false}// Primitiveselse if (!Objects.isArray(objectA) && !Objects.isObject(objectA)) {return objectA === objectB}// Object or arrayelse {const compareObject = (objectA: any, objectB: any): boolean => {if (Object.keys(objectA).length !== Object.keys(objectB).length) return false
for (const propertyName of Object.keys(objectA)) {const valueA = objectA[propertyName]const valueB = objectB[propertyName]
if (!Objects.isEqual(valueA, valueB)) {return false}}
return true}const compareArray = (arrayA: any[], arrayB: any[]): boolean => {if (arrayA.length !== arrayB.length) return false
for (const index in arrayA) {const valueA = arrayA[index]const valueB = arrayB[index]
if (!Objects.isEqual(valueA, valueB)) {return false}}
return true}if (Objects.isObject(objectA)) {return compareObject(objectA, objectB)} else {return compareArray(objectA, objectB)}}}}

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

你有4+1类解决方案


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

如果你很匆忙,99%的正确率是有效的。

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

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

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

我的父亲类比

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

请注意,英语语法中实际上有一个正确(标准方式)订单的形容词,应该是一个“英俊的高个子”,但是如果你盲目地假设iOS8的Javascript引擎Safari也遵守相同的语法,盲目地,你会冒着能力的风险!


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

很好,如果你正在学习。

例子是atmin解决方案

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

我只鼓励人们这样做,如果他们正在练习并且代码不会投入生产。这是重新发明轮子有理由的唯一情况。

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

我的父亲类比

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

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

2.1你有限的范围DIY比较器

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

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

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

3)使用equal函数的库版本

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

例子是_.equal的洋葱,已经在Coolaj86的答案或Angular或Ember的托尼·哈维的回答或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。

另一个选项是使用equals ofRamda图书馆

const c = {a: 1, b: 2};const d = {b: 2, a: 1};R.equals(c, d); //=> true

const obj = {name: "Carl",age: 15}const obj2 = {name: "Carl",age: 15,}

const compareObj = (objects) => {const res =  objects.map((item) => {return Object.entries(item).flat().join()})return res.every((a) => {return a === res[0]})}
console.log(compareObj([obj,obj2]))

如果你真的想比较并返回两个对象的差异。您可以使用此包:https://www.npmjs.com/package/deep-diff

或者只是使用这个包使用的代码

https://github.com/flitbit/diff/blob/master/index.js

只是不要将其转换为字符串进行比较。

这里有一个使用ES6+的解决方案

// this comparison would not work for function and symbol comparisons// this would only work best for compared objects that do not belong to same address in memory// Returns true if there is no difference, and false otherwise

export const isObjSame = (obj1, obj2) => {if (typeof obj1 !== "object" && obj1 !== obj2) {return false;}
if (typeof obj1 !== "object" && typeof obj2 !== "object" && obj1 === obj2) {return true;}
if (typeof obj1 === "object" && typeof obj2 === "object") {if (Array.isArray(obj1) && Array.isArray(obj2)) {if (obj1.length === obj2.length) {if (obj1.length === 0) {return true;}const firstElemType = typeof obj1[0];
if (typeof firstElemType !== "object") {const confirmSameType = currentType =>typeof currentType === firstElemType;
const checkObjOne = obj1.every(confirmSameType);const checkObjTwo = obj2.every(confirmSameType);
if (checkObjOne && checkObjTwo) {// they are primitves, we can therefore sort before and compare by index// use number sort// use alphabet sort// use regular sortif (firstElemType === "string") {obj1.sort((a, b) => a.localeCompare(b));obj2.sort((a, b) => a.localeCompare(b));}obj1.sort((a, b) => a - b);obj2.sort((a, b) => a - b);
let equal = true;
obj1.map((element, index) => {if (!isObjSame(element, obj2[index])) {equal = false;}});
return equal;}
if ((checkObjOne && !checkObjTwo) ||(!checkObjOne && checkObjTwo)) {return false;}
if (!checkObjOne && !checkObjTwo) {for (let i = 0; i <= obj1.length; i++) {const compareIt = isObjSame(obj1[i], obj2[i]);if (!compareIt) {return false;}}
return true;}
// if()}const newValue = isObjSame(obj1, obj2);return newValue;} else {return false;}}
if (!Array.isArray(obj1) && !Array.isArray(obj2)) {let equal = true;if (obj1 && obj2) {const allKeys1 = Array.from(Object.keys(obj1));const allKeys2 = Array.from(Object.keys(obj2));
if (allKeys1.length === allKeys2.length) {allKeys1.sort((a, b) => a - b);allKeys2.sort((a, b) => a - b);
allKeys1.map((key, index) => {if (key.toLowerCase() !== allKeys2[index].toLowerCase()) {equal = false;return;}
const confirmEquality = isObjSame(obj1[key], obj2[key]);
if (!confirmEquality) {equal = confirmEquality;return;}});}}
return equal;
// return false;}}};

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

一些测试取自@EbrahimByagowi这里的答案。

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

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

虽然这个问题已经得到了充分的回答,但我缺少一种方法:toJSON接口。

通常你想通过字符串化来比较对象,因为这是最快的方法。但是由于属性的顺序,比较经常被认为是false。

const obj1 = {a: 1,b: 2,c: {ca: 1,cb: 2}}
const obj2 = {b: 2, // changed order with aa: 1,c: {ca: 1,cb: 2}}
JSON.stringify(obj1) === JSON.stringify(obj2) // false

显然,对象被认为是不同的,因为属性ab的顺序不同。

为了解决这个问题,您可以实现toJSON接口,并定义一个确定性输出。

const obj1 = {a: 1,b: 2,c: {ca: 1,cb: 2},toJSON() {return {a: this.a,b: this.b,c: {ca: this.c.ca,cb: this.c.ca}}}}
const obj2 = {b: 2,a: 1,c: {ca: 1,cb: 2},toJSON() {return {a: this.a,b: this.b,c: {ca: this.c.ca,cb: this.c.ca}}}}
JSON.stringify(obj1) === JSON.stringify(obj2) // true

等等:obj1obj2的字符串表示是相同的。

TIP

如果您无法访问对象的直接生成,您可以简单地附加toJSON函数:

obj1.toJSON = function() {return {a: this.a,b: this.b,c: {ca: this.c.ca,cb: this.c.ca}}}
obj2.toJSON = function() {return {a: this.a,b: this.b,c: {ca: this.c.ca,cb: this.c.ca}}}
JSON.stringify(obj1) === JSON.stringify(obj2) // true

我之前添加了一个答案,但它并不完美,但这一个将检查对象的相等性

function equalObjects(myObj1, myObj2){
let firstScore = 0;let secondScore = 0;let index=0;
let proprtiesArray = [];let valuesArray = [];

let firstLength = 0;let secondLength = 0;

for (const key in myObj1) {if (myObj1.hasOwnProperty(key)) {firstLength += 1;proprtiesArray.push(key);valuesArray.push(myObj1[key]);firstScore +=1;}}
for (const key in myObj2) {if (myObj2.hasOwnProperty(key)) {secondLength += 1;if (valuesArray[index] === myObj2[key] && proprtiesArray[index] === key) {secondScore +=1;}//console.log(myObj[key]);        
index += 1;}    
}
if (secondScore == firstScore && firstLength === secondLength) {console.log("true", "equal objects");return true;} else {console.log("false", "not equal objects");return false;}
}
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName':'Lovelace'});
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName1':'Lovelace'});
equalObjects({'firstName':'Ada','lastName':'Lovelace'},{'firstName':'Ada','lastName':'Lovelace', 'missing': false});

在对象(没有方法)中,我们需要检查nested ObjectsArraysprimitive types。对象可以有其他对象和数组(数组也可以包括其他对象和数组),所以我们可以使用递归函数,如下所示: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}

在React中,您可以使用“react-快速比较”中的isEqual。此答案可能不适用于普通JavaScript,但在您使用React的情况下可能会很有用。

console.log(isEqual({ hello: 'world' }, { hello: 'world' })) // returns true

React最快的深度相等比较。也非常快速的通用深度比较。非常适合React.memo。

更多信息可以在这里找到:https://www.npmjs.com/package/react-fast-compare

纯JS方法:我的答案是基于生成一个字符串,无论属性顺序是否相同,该字符串都返回相同的值。设置对象可用于切换大小写和空格的存在是否重要。(为了避免失去焦点,我没有包括那些支持函数,或者我想应该在任何实用程序集中的isObject。)

这里也没有显示,但是为了减少字符串比较时间,如果对象很大并且你想加快比较速度,你也可以散列字符串并比较子字符串;这只对非常大的对象有意义(当然,错误相等的可能性很小)。

然后,您可以比较genObjStr(Obj1)?=genObjStr(Obj2)

function genObjStr(obj, settings) {// Generate a string that corresponds to an object guarenteed to be the same str even if// the object have different ordering. The string would largely be used for comparison purposes
var settings = settings||{};var doStripWhiteSpace = defTrue(settings.doStripWhiteSpace);var doSetLowerCase = settings.doSetLowerCase||false;
if(isArray(obj)) {var vals = [];for(var i = 0; i < obj.length; ++i) {vals.push(genObjStr(obj[i], settings));}vals = arraySort(vals);return vals.join(`,`);
} else if(isObject(obj)) {
var keys = Object.keys(obj);keys = arraySort(keys);
var vals = [];for(var key of keys) {        
var value = obj[key];        
value = genObjStr(value, settings);
if(doStripWhiteSpace) {key = removeWhitespace(key);var value = removeWhitespace(value);};if(doSetLowerCase) {key = key.toLowerCase();value = value.toLowerCase();}
vals.push(value);}var str = JSON.stringify({keys: keys, vals: vals});return str} else {if(doStripWhiteSpace) {obj = removeWhitespace(obj);};if(doSetLowerCase) {obj = obj.toLowerCase();}return obj}

}

var obj1 = {foo: 123, bar: `Test`};var obj2 = {bar: `Test`, foo: 123};
console.log(genObjStr(obj1) == genObjStr(obj1))
let std1 = {name: "Abhijeet",roll: 1}
let std2 = {name: "Siddharth",roll: 2}
console.log(JSON.stringify(std1) === JSON.stringify(std2))

如果您的问题是检查两个对象是否相等,那么这个函数可能会有用

function equals(a, b) {const aKeys = Object.keys(a)const bKeys = Object.keys(b)if(aKeys.length != bKeys.length) {return false}for(let i = 0;i < aKeys.length;i++) {if(aKeys[i] != bKeys[i]) {return false}}for(let i = 0;i < aKeys.length;i++) {if(a[aKeys[i]] != b[bKeys[i]]) {return false}}return true}

首先我们检查这些对象的键列表的长度是否相同,如果不是,我们返回false为了检查两个对象是否相等,它们必须具有相同的键(=名称)和相同的键值,所以我们得到了ObjectivA和ObjectivB的所有键,然后我们检查它们是否相等,一旦我们发现两个键不相等,然后我们返回false然后当所有键都相等时,我们循环遍历其中一个对象的一个键,然后我们检查它们是否相等,一旦它们不相等,我们返回false,两个循环完成后,这意味着它们相等,我们返回true注意:此函数仅适用于没有函数的对象

使用JSON.stringify()并不总是可靠的。所以这个方法是你问题的最佳解决方案IMO

首先,,没有通用的方法来确定对象是否相等!

但是有一个概念叫做浅相等比较。有一个npm类库可以帮助你使用这个概念

示例

const shallowequal = require('shallowequal'); 
const object = { 'user': 'fred' };const other = { 'user': 'fred' };
// Referential Equality Comparison (`strict ===`)object === other; // → false
// Shallow Equality Comparisonshallowequal(object, other); // → true

如果您想知道如何创建shallowEqual比较方法,请参阅这里。它来自开源fbjs Facebook库。


浅相等比较

shallowequal(obj1, obj2)

shallowEqual在两个值(即obj1obj2)之间执行浅相等比较以确定它们是否等效。

相等是通过迭代给定obj1上的键来执行的,只要任何键的值在obj1obj2之间不严格相等,就返回false。否则,只要所有键的值严格相等,就返回true

我发现比较两个javascript对象而忽略属性顺序中的价值观的一个简单方法是使用JSON stringify替换函数:

const compareReplacer = (key, value) => {if(typeof value === 'object' && !(value instanceof Array))return Object.entries(value).sort();return value;}export const compareObjects = (a, b) => JSON.stringify(a, compareReplacer) === JSON.stringify(b, compareReplacer);

这将在每一步对属性进行排序,以便字符串结果与属性顺序保持不变。有人可能以前做过这个,但我只是想我会分享它incase not:)。

字符串化两个对象并比较

return (JSON.stringify(obj1) === JSON.stringify(obj2))

这将返回true或false

2022年:

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

步骤:

  1. 把物体弄平
  2. 简单地比较两个扁平的物体并寻找差异

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

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 keysfor (let key in flat1) {if (!(key in flat2))return false}return true}

您还可以使用该方法来获取obj1obj2之间的Diff对象。

查看详细信息:两个对象之间的通用深度差异

添加此版本,因为它处理日期并具有显示其工作原理的流程图。

简而言之:

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); //truecompare(a, c); //truecompare(a, d); //false
//----- BUT! -----JSON.stringify(a) === JSON.stringify(b); //false
//----- AND -----compare({}, {}); //truecompare({}, undefined); //falsecompare(undefined, undefined); //truecompare(undefined, ''); //falsecompare(undefined, null); //falsecompare(null, null); //truecompare('', ''); //true
const isEqual = function (var1, var2) {if (typeof var1 === 'object' && typeof var2 === 'object') {// Checking equality for each of the inner values of the objectsconst keys = [...new Set([...Object.keys(var1),...Object.keys(var2)])];return keys.every(key => isEqual(var1[key], var2[key]) && isEqual(var2[key], var1[key]));} else { // Primitive types (number, boolean etc..)return var1 === var2; // Normal equality}}

所以我在这里看到了很多很棒的答案,但是我找不到一个简短的函数,所以我创建了一个。这将检查两个变量的相等性,无论类型如何。希望你能喜欢