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

问题描述 投票:543回答:55

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

Stack Overflow问题Is there any kind of hashCode function in JavaScript?与此问题类似,但需要更多的学术答案。上面的场景说明了为什么需要有一个,我想知道是否有任何等效的解决方案。

javascript object equals hashcode
55个回答
161
投票

简短的回答

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

答案很长

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

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

在上面的例子中,c对于确定MyClass的任何两个实例是否相等并不重要,只有ab很重要。在某些情况下,c可能会因实例而异,但在比较期间并不显着。

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

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

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

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

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


17
投票

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

MyClass.equals()

注意:

  • 你需要用你的对象替换JSON.stringify({a: val1}) === JSON.stringify({a: val2})and val1
  • 对于对象,您必须递归(按键)对两个侧对象进行排序

16
投票

在Node.js中,您可以使用其本机val2。更多信息: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 / true而不是返回错误的示例:

false

12
投票

我使用这个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; }; 函数来生成与JSON相当的我的对象的副本:

comparable
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>功能)。例如。

is

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

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

9
投票

Heres是ES6 / ES2015中使用功能风格方法的解决方案:

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

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


6
投票

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

demo available here

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


6
投票

你可以使用underscore.js库中的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; }

这是一个例子:

_.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


5
投票

很多人都没有意识到的这个问题的简单解决方案是对JSON字符串进行排序(每个字符)。这通常也比这里提到的其他解决方案更快:

http://underscorejs.org/#isEqual

关于此方法的另一个有用的事情是您可以通过将“replacer”函数传递给JSON.stringify函数(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('')); } )来过滤比较。以下内容仅比较名为“derp”的所有对象键:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter

3
投票

我建议不要使用散列或序列化(如JSON解决方案所示)。如果您需要测试两个对象是否相等,那么您需要定义equals的含义。可能是两个对象中的所有数据成员都匹配,或者可能是内存位置必须匹配(意味着两个变量都引用内存中的同一对象),或者每个对象中只有一个数据成员必须匹配。

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

在那种情况下,我将“相等”定义为id值匹配。鉴于每个实例都具有唯一ID,这可用于强制匹配对象也占据相同内存位置的想法。虽然没有必要。


3
投票

需要比发布的更通用的对象比较功能,我做了以下工作。批评赞赏......

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

3
投票

如果您要比较JSON对象,可以使用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; }

https://github.com/mirek/node-rus-diff

用法:

npm install rus-diff

如果两个对象不同,则返回与对象类似的MongoDB 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 equal console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }


455
投票

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

_.isEqual(object, other);

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

注意:此前这个答案推荐使用Underscore.js,但是lodash在修复错误和解决问题方面做得更好。


3
投票

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

{$rename:{...}, $unset:{...}, $set:{...}}

该算法分为两部分; equals函数本身和一个函数,用于查找数组/对象中属性的数字索引。只需要查找函数,因为indexof只查找数字和字符串而没有对象。

人们可以这样称呼它:

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

该函数返回true或false,在这种情况下为true。算法允许比较非常复杂的对象:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

上面的示例将返回true,即使属性具有不同的顺序。需要注意的一个小细节:此代码还检查相同类型的两个变量,因此“3”与3不同。


3
投票

只是想利用一些es6功能贡献我的对象比较版本。它没有考虑订单。将所有if / else的转换为三元后,我带来了以下内容:

({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"]})

2
投票

如果两个对象对于所有属性具有所有相同的值并且对所有嵌套对象和数组递归,则认为两个对象相等是有用的。我还认为以下两个对象是相同的:

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;

        }
    )
}

类似地,数组可以具有“缺失”元素和未定义元素。我会同样对待那些:

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):Source codeUnit Test


2
投票

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

  1. 您可以控制要比较的对象,并且只有原始值(即不是嵌套对象,函数等)。
  2. 您的浏览器支持Test Runner here

这应被视为一个简单策略的示范。

Object.keys

2
投票

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

这比较:1)自有属性的数量相等,2)关键名称的相等性,3)如果bCompareValues == true,相应属性值的相等性及其类型(三重相等)

/**
 * 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 elements
    if( 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 keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}

2
投票

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

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

一旦比较了密钥,一个简单的额外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); }; 循环就足够了。

复杂度是O(N * N),N是键的数量。

我希望/猜测我​​定义的对象不会超过1000个属性......


2
投票

我知道这有点旧,但我想为这个问题添加一个我想出的解决方案。我有一个对象,我想知道它的数据何时发生变化。 “类似于Object.observe的东西”,我做的是:

for..in

这里可以复制并创建另一组数组来比较值和键。它非常简单,因为它们现在是数组,如果对象具有不同的大小,则返回false。


2
投票

从我的个人图书馆退出,我多次用于我的工作。以下函数是一个宽泛的递归深度相等,不检查

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

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

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

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

2
投票

这是stringify技巧的一个版本,它可以减少输入,并且可以在很多情况下进行简单的JSON数据比较。

/** 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 [email protected], 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 fails
	var 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 array
		if( !Array.isArray(objB) ) {
			return false;
		}
	} else if( Array.isArray(objB) ) {
		//objA is not array, objB is an array
		return 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 objB
	for (i in objA) {
		//Protect against inherited properties
		if(objA.hasOwnProperty(i)) {
			if(objB.hasOwnProperty(i)) {
				// Check if deep equal is valid
				if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
					return false;
				}
			} else if(objA[i] != null) {
				//ignore null values in objA, that objB does not have
				//else fails
				return false;
			}
		}
	}
	
	// Check if objB has additional values, that objA do not, fail if so
	for (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 fails
				return false;
			}
		}
	}
	
	// End of all checks
	//--------------------------------------------
	// By reaching here, all iteration scans have been done.
	// and should have returned false if it failed
	return 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 checks
		simpleRecusiveDeepEqual( 0, null ) ||
		simpleRecusiveDeepEqual( "", null ) ||
		// Last "false" exists to make the various check above easy to comment in/out
		false
	) {
		alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
	} else { 
		//added this last line, for SO snippet alert on success
		alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
	}
})();

2
投票

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

首先按键对它们的键名进行排序。

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

然后只需使用字符串来比较它们。

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)

145
投票

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

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

x === y; // => false
x === 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); // => false
kingOfSpades.equals(new Card(13, "S")); // => true

2
投票

对于那些使用NodeJS的人来说,在本机Util库上有一个名为JSON.stringify(objectOne) === JSON.stringify(objectTwo) 的方便方法可以实现这一点。

isDeepStrictEqual

const util = require('util'); const foo = { hey: "ho", lets: "go" } const bar = { hey: "ho", lets: "go" } foo == bar // false util.isDeepStrictEqual(foo, bar) // true


72
投票

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

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

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>

58
投票

这是我的版本。它正在使用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 } }));

47
投票

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

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

alert(JSON.encode(obj1)===JSON.encode(obj2));

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


31
投票

短功能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);
}

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

但是:ぁzxswい

理由:

由于http://jsfiddle.net/CU3vb/3/的属性被逐个复制到克隆,因此它们在克隆中的顺序将被保留。当obj1的属性被复制到克隆时,由于obj2中已存在的属性将被简单地覆盖,它们在克隆中的顺序将被保留。


17
投票

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

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

obj1

你可能需要这样做:

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; } ),但你明白了。

正如FOR指出的那样,您可能必须根据自己的目的进行调整,例如:不同的类可能有不同的“相等”定义。如果你只是处理普通对象,上面就足够了,否则自定义var a = { foo : { fu : "bar" } }函数可能就好了。

© www.soinside.com 2019 - 2024. All rights reserved.