严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
当前回答
如果您有一个方便的深度复制函数,您可以使用下面的技巧来使用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中,你可以使用它的原生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;
};
这是我的版本。它正在使用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(obj1) === JSON.stringify(obj2)一样高效,但是JSON.stringify(obj2)。如果顺序不相同(如此处所述),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 length for (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 different hat : { cap : ['something', null ], helmet : [ 'triple eight', 'pro-tec' ] } }, { beers : [ 'budweiser', 'busch' ], wines : [ 'barefoot', 'yellow tail' ] } ]; console.log(deep_equal(obj1, obj2)); // true console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false console.log(deep_equal([], [])); // true console.log(deep_equal({}, {})); // true console.log(deep_equal([], {})); // false
如果你想增加对Function, Date和RegExp的支持,你可以在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();
}
我不知道是否有人发布过类似的东西,但这是我做的一个检查对象相等的函数。
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;
}
而且,它是递归的,所以它也可以检查深度相等,如果你这么称呼它的话。
你是在测试两个对象是否相等吗?即:它们的属性是相等的?
如果是这样的话,你可能会注意到这种情况:
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所指出的,你可能需要根据自己的目的进行调整,例如:不同的类可能对“equal”有不同的定义。如果您只是处理普通对象,上述方法可能就足够了,否则可以使用自定义MyClass.equals()函数。