严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
当前回答
虽然这个问题已经得到了充分的回答,但我还缺少一种方法:toJSON接口。
通常你想通过字符串化来比较object,因为这是最快的方法。但往往比较被认为是假的,因为性质的顺序。
const obj1 = {
a: 1,
b: 2,
c: {
ca: 1,
cb: 2
}
}
const obj2 = {
b: 2, // changed order with a
a: 1,
c: {
ca: 1,
cb: 2
}
}
JSON.stringify(obj1) === JSON.stringify(obj2) // false
显然,对象被认为是不同的,因为属性a和b的顺序不同。
要解决这个问题,可以实现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
瞧:obj1和obj2的字符串表示被认为是相同的。
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
其他回答
我看到了意大利式的代码答案。 不使用任何第三方的lib,这是非常容易的。
首先,按键对两个对象进行排序。
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)
如果您有一个方便的深度复制函数,您可以使用下面的技巧来使用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中已经存在的属性将被简单地覆盖,它们在克隆对象中的顺序将被保留。
这里有一个非常基本的方法来检查对象的“值是否相等”。
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 equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects are considered equivalent
return true;
}
// Outputs: true
console.log(isEquivalent(john, bobby));
演示 - JSFiddle
如你所见,为了检查对象的“值是否相等”,我们本质上必须遍历对象中的每个属性,以查看它们是否相等。虽然这个简单的实现适用于我们的示例,但有很多情况它无法处理。例如:
如果其中一个属性值本身就是一个对象呢? 如果属性值之一是NaN(中唯一的值 JavaScript不等于它自己?) 如果a有一个值为undefined的属性,而b没有呢 这个属性(因此计算为undefined?)
对于一个健壮的检查对象“值是否相等”的方法,最好依赖于一个经过良好测试的库,它涵盖了各种边缘情况,如下划线。
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
// Outputs: true
console.log(_.isEqual(john, bobby));
演示 - JSFiddle
这是我的版本。它正在使用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 } }));
对象是否相等检查:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())
上面的测试还适用于对象数组,在这种情况下使用http://www.w3schools.com/jsref/jsref_sort.asp中记录的排序函数
对于具有平面JSON模式的小型数组可能足够了。