严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
当前回答
当然,当我们在它的时候,我会抛出我自己对车轮的重新发明(我为辐条和使用的材料的数量感到自豪):
////////////////////////////////////////////////////////////////////////////////
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,但当然,对于一个差异嵌套很深的大对象,它可能不那么有效。在我自己的场景中,良好地处理嵌套数组非常重要。
希望它能帮助需要这种“轮子”的人。
其他回答
这取决于你对平等的定义。因此,作为类的开发人员,要由您来定义它们的相等性。
有时会使用一种情况,如果两个实例指向内存中的相同位置,则认为它们是“相等的”,但这并不总是您想要的。例如,如果我有一个Person类,如果两个Person对象具有相同的Last Name、First Name和Social Security Number(即使它们指向内存中的不同位置),我可能会认为它们是“相等的”。
另一方面,我们不能简单地说两个对象是相等的,如果它们的每个成员的值都相同,因为,有时,你并不想这样。换句话说,对于每个类,由类开发人员定义组成对象“标识”的成员并开发适当的相等操作符(通过重载==操作符或Equals方法)。
Saying that two objects are equal if they have the same hash is one way out. However you then have to wonder how the hash is calculated for each instance. Going back to the Person example above, we could use this system if the hash was calculated by looking at the values of the First Name, Last Name, and Social Security Number fields. On top of that, we are then relying on the quality of the hashing method (that's a huge topic on its own, but suffice it to say that not all hashes are created equal, and bad hashing methods can lead to more collisions, which in this case would return false matches).
我发现比较两个javascript对象的值而忽略属性顺序的一个简单方法是使用JSON stringify replacer函数:
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);
这将在每一步对属性进行排序,以便字符串结果对属性顺序不变。以前可能有人这样做过,但我只是想分享一下,以防万一:)。
这是我的版本。它正在使用new Object。ES5中引入的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 } }));
当然,当我们在它的时候,我会抛出我自己对车轮的重新发明(我为辐条和使用的材料的数量感到自豪):
////////////////////////////////////////////////////////////////////////////////
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,但当然,对于一个差异嵌套很深的大对象,它可能不那么有效。在我自己的场景中,良好地处理嵌套数组非常重要。
希望它能帮助需要这种“轮子”的人。
最简单和逻辑的解决方案,比较一切像对象,数组,字符串,Int…
JSON。stringify({a: val1}) == JSON。stringify ({a: val2})
注意:
你需要用你的Object替换val1和val2 对于对象,必须对两侧对象进行递归排序(按键)