严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
当前回答
我需要模拟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' });
其他回答
EDIT: This method is quite flawed, and is rife with its own issues. I don't recommend it, and would appreciate some down-votes! It is problematic because 1) Some things can not be compared (i.e. functions) because they can not be serialized, 2) It isn't a very fast method of comparing, 3) It has ordering issues, 4) It can have collision issues/false positives if not properly implemented, 5) It can't check for "exactness" (===), and instead is based of value equality, which is oftentimes not what is desired in a comparison method.
这个问题的一个简单解决方案是对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传递一个“replace”函数来筛选比较。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;
});
这是我的版本。它正在使用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 } }));
这是一个经典的javascript问题!我创建了一个方法来检查深度对象是否相等,其特性是能够从比较中选择要忽略的属性。 参数是要比较的两个对象,加上一个可选的字符串化属性-忽略相对路径数组。
function isObjectEqual( o1, o2, ignorePropsArr=[]) {
// Deep Clone objects
let _obj1 = JSON.parse(JSON.stringify(o1)),
_obj2 = JSON.parse(JSON.stringify(o2));
// Remove props to ignore
ignorePropsArr.map( p => {
eval('_obj1.'+p+' = _obj2.'+p+' = "IGNORED"');
});
// compare as strings
let s1 = JSON.stringify(_obj1),
s2 = JSON.stringify(_obj2);
// return [s1==s2,s1,s2];
return s1==s2;
}
// Objects 0 and 1 are exact equals
obj0 = { 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) // true
isObjectEqual(obj0,obj2) // false
isObjectEqual(obj0,obj2,['price']) // true
isObjectEqual(obj0,obj3,['price']) // false
isObjectEqual(obj0,obj3,['price','candles.A']) // true
isObjectEqual(obj0,obj4,['price','RSIs[1]']) // true
虽然这个问题已经有很多答案了。我只是想提供另一种实现方法:
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.
const isEqual = function (var1, var2) {
if (typeof var1 === 'object' && typeof var2 === 'object') {
// Checking equality for each of the inner values of the objects
const 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
}
}
我在这里看到了很多很好的答案,但我找不到一个简短的函数,所以我创建了一个。 这将检查两个变量是否相等,无论其类型如何。 希望你看得合适。