提问人: 提问时间:10/14/2008 最后编辑:Peter Mortensen 更新时间:10/4/2023 访问量:877005
如何确定两个 JavaScript 对象的相等性?
How can I determine equality for two JavaScript objects?
问:
严格相等运算符将告诉您两个对象类型是否相等。但是,有没有办法判断两个对象是否相等,就像 Java 中的哈希代码值一样?
Stack Overflow 问题 JavaScript 中是否有任何类型的 hashCode 函数? 与这个问题类似,但需要更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有任何等效的解决方案。
答:
您是否正在尝试测试两个对象是否相等?即:它们的性质是相等的吗?
如果是这种情况,您可能已经注意到这种情况:
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 所指出的,您可能必须根据自己的目的进行调整,例如:不同的类可能对“平等”有不同的定义。如果您只是使用普通对象,上述内容可能就足够了,否则自定义函数可能是要走的路。MyClass.equals()
评论
如果您使用的是 JSON 库,则可以将每个对象编码为 JSON,然后比较生成的字符串是否相等。
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
注意:虽然这个答案在许多情况下都有效,但正如一些人在评论中指出的那样,由于各种原因,它是有问题的。在几乎所有情况下,您都希望找到一个更强大的解决方案。
评论
简短的回答
简单的答案是:不,没有通用的方法可以确定一个对象与另一个对象相等。例外情况是,严格来说,对象是无类型的。
长答案
这个概念是一个 Equals 方法,该方法比较对象的两个不同实例,以指示它们在值级别上是否相等。但是,由特定类型来定义应如何实现方法。对具有基元值的属性进行迭代比较可能还不够:对象可能包含与相等性无关的属性。例如Equals
function MyClass(a, b)
{
var c;
this.getCLazy = function() {
if (c === undefined) c = a * b // imagine * is really expensive
return c;
}
}
在上面的这个例子中,确定MyClass的任何两个实例是否相等并不重要,只有和重要。在某些情况下,实例之间可能会有所不同,但在比较过程中并不显着。c
a
b
c
请注意,当成员本身也可能是某种类型的实例时,此问题适用,并且每个实例都需要具有确定相等性的方法。
更复杂的是,在JavaScript中,数据和方法之间的区别是模糊的。
对象可以引用要作为事件处理程序调用的方法,这可能不会被视为其“值状态”的一部分。而另一个对象很可能被分配一个执行重要计算的函数,从而使这个实例与其他实例不同,仅仅是因为它引用了一个不同的函数。
如果一个对象的现有原型方法被另一个函数覆盖,该怎么办?它是否仍可被视为等同于其他方面相同的另一个实例?这个问题只能在每种类型的每种特定情况下回答。
如前所述,例外是严格无类型的对象。在这种情况下,唯一明智的选择是对每个成员进行迭代和递归比较。即便如此,人们也不得不问一个函数的“价值”是什么?
评论
_.isEqual(obj1, obj2);
.equals
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
评论
{x:1, y:2}
{y:2, x:1}
需要一个比发布更通用的对象比较功能,我制作了以下内容。评论赞赏...
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;
}
评论
Object.prototype
Object.equals = function(aObj, bObj) {...}
为什么要重新发明轮子?试试 Lodash。它有许多必备的函数,例如 isEqual()。
_.isEqual(object, other);
它将使用 ECMAScript 5 和本机优化(如果浏览器中可用)暴力检查每个键值 - 就像本页的其他示例一样。
注意:以前这个答案推荐了下划线.js,但 lodash 在修复错误和解决一致性问题方面做得更好。
评论
如果您手头有深度复制功能,则可以使用以下技巧在匹配属性顺序的同时仍可使用: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
评论
这是我的版本。它使用了 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 } }));
评论
objectEquals([1,2,undefined],[1,2])
返回true
objectEquals([1,2,3],{0:1,1:2,2:3})
也返回 -- 例如,没有类型检查,只有键/值检查。true
objectEquals(new Date(1234),1234)
返回true
如果您在 AngularJS 中工作,该函数将确定两个对象是否相等。在 Ember.js 中使用 .angular.equals
isEqual
angular.equals
- 有关此方法的更多信息,请参阅文档或源代码。它也对数组进行了深入的比较。- Ember.js - 有关此方法的更多信息,请参阅文档或源代码。它不会对数组进行深入比较。
isEqual
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>
如果要比较 JSON 对象,可以使用 https://github.com/mirek/node-rus-diff
npm install rus-diff
用法:
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 } }
如果两个对象不同,则返回一个与MongoDB兼容的类似对象。{$rename:{...}, $unset:{...}, $set:{...}}
在 Node.js 中,您可以使用其原生 .更多的信息: http://nodejs.org/api/assert.htmlrequire("assert").deepStrictEqual
例如:
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;
};
评论
Chai
也有这个功能。在这种情况下,您将使用:var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;
error.name
"AssertionError [ERR_ASSERTION]"
if (error.code === 'ERR_ASSERTION') {
deepStrictEqual
strictEqual
编辑:这种方法存在很大缺陷,并且充斥着自己的问题。我不推荐它,并希望投一些反对票!这是有问题的,因为 1) 有些东西不能比较(即函数),因为它们不能序列化,2) 它不是一种非常快速的比较方法,3) 它有排序问题,4) 如果实施不当,它可能会有冲突问题/误报,5) 它不能检查“精确性”(),而是基于值相等, 这通常不是比较方法所期望的。===
许多人没有意识到的这个问题的一个简单解决方案是对 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(''));
}
此方法的另一个有用之处在于,您可以通过将“replacer”函数传递给 JSON.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;
});
评论
areEqual({a: 'b'}, {b: 'a'})
然后得到?true
以下是 ES6/ES2015 中使用函数式方法的解决方案:
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;
}
}
评论
简短的功能实现: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);
}
评论
reduce
every
Object.keys(x).every(key => deepEqual(x[key], y[key]))
: (x === y)
: (x === y && (x != null && y != null || x.constructor === y.constructor))
我使用这个函数来生成我的对象的副本,这些副本是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');
如果发现差异,则会记录字符串,从而使差异变得可观察:
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}}.
评论
我遇到了同样的问题,并决定编写自己的解决方案。但是因为我还想将数组与对象进行比较,反之亦然,所以我设计了一个通用解决方案。我决定将函数添加到原型中,但可以很容易地将它们重写为独立函数。代码如下:
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;
}
该算法分为两部分;equals 函数本身和一个函数,用于查找数组/对象中属性的数值索引。只需要 find 函数,因为 indexof 只查找数字和字符串,而不查找对象。
可以这样称呼它:
({a: 1, b: "h"}).equals({a: 1, b: "h"});
该函数返回 true 或 false,在本例中为 true。 该算法允许在非常复杂的对象之间进行比较:
({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"]})
上面的示例将返回 true,即使属性具有不同的顺序。需要注意的一个小细节:此代码还检查相同类型的两个变量,因此“3”与 3 不同。
只是想利用一些 es6 功能贡献我的对象比较版本。它不考虑订单。将所有 if/else 转换为三元后,我得到了以下内容:
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;
}
)
}
我不知道是否有人发布过类似的东西,但这是我用来检查对象等式的函数。
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;
}
此外,它是递归的,所以它也可以检查深度相等性,如果你这样称呼它的话。
评论
最简单和合乎逻辑的解决方案,用于比较所有内容,如对象、数组、字符串、整数......
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
注意:
- 您需要替换 和 替换为您的对象
val1
val2
- 对于对象,您必须对两端对象进行递归排序(按键)
评论
您可以从下划线 .js 库中使用。_.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
从这里查看官方文档:http://underscorejs.org/#isEqual
假设对象中属性的顺序没有改变。
JSON.stringify() 适用于深层和非深层两种类型的对象,不太确定性能方面:
var object1 = {
key: "value"
};
var object2 = {
key: "value"
};
var object3 = {
key: "no value"
};
console.log('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));
console.log('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));
评论
对于那些使用 Node 的人来说,有一种方便的方法可以在本机库上调用来实现这一点。isDeepStrictEqual
util
const util = require('util');
const obj1 = {
foo: "bar",
baz: [1, 2]
};
const obj2 = {
foo: "bar",
baz: [1, 2]
};
obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true
https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2
评论
ES6:我可以完成的最小代码是这个。它通过字符串化所有键值数组排序来递归地进行深度比较,唯一的限制是没有方法或符号进行比较。
const compareObjects = (a, b) => {
let s = (o) => Object.entries(o).sort().map(i => {
if(i[1] instanceof Object) i[1] = s(i[1]);
return i
})
return JSON.stringify(s(a)) === JSON.stringify(s(b))
}
console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));
重要:这个函数在一个 ARRAY 中执行 JSON.stringfy,其中键已排序,而不是在它本身的对象中:
- [“a”, [“b”, 1]]
- [“b”, 4]
评论
var object1 = {name: "humza" , gender : "male", age: 23}
var object2 = {name: "humza" , gender : "male", age: 23}
var result = Object.keys(object1).every((key) => object1[key] === object2[key])
如果 object1 在 object2 上具有相同的值,则结果将为 true。
评论
如何确定分部对象 (Partial<T>) 是否等于打字稿中的原始对象 (T)。
function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}
P.S. 最初,我打算创建一个带有答案的新问题。但是这样的问题已经存在并被标记为重复。
这个问题已经有 30 多个答案。我将总结和解释它们(用“我父亲”的类比)并添加我建议的解决方案。
您有 4+1 类解决方案:
1)使用hacky不完整的快速单行
如果您赶时间并且 99% 的正确性有效,那就太好了。
例如,Pratik Bhalodiya、Joel Anair 或 或其他方法将对象转换为 String,然后使用逐个字符比较两个 String。JSON.stringify()
JSON.encode
.toString()
===
但是,缺点是 String 中没有 Object 的全局标准唯一表示形式。例如: 并且是平等的。{ a: 5, b: 8}
{b: 8 and a: 5 }
- 优点:快,快。
- 缺点: 希望有效!如果环境/浏览器/引擎记住了对象的顺序(例如 Chrome/V8)并且键的顺序不同,它将不起作用(感谢 Eksapsy。所以,完全不能保证。在大型物体中,性能也不会很好。
我父亲的比喻
当我说到我的父亲时,“我高大帅气的父亲”和“我帅气的高个子父亲”是同一个人!但是这两个字符串是不一样的。
请注意,英语语法中实际上有一个正确的(标准方式)形容词顺序,它说它应该是一个“英俊的高个子”,但如果你盲目地假设iOS 8 Safari的Javascript引擎也盲目地遵守相同的语法,你就是在冒着能力的风险!#WelcomeToJavascriptNonStandards
2)编写自己的DIY递归函数
如果你正在学习,那就太好了。
例如,atmin的解决方案。
最大的缺点是你肯定会错过一些边缘情况。您是否考虑过对象值中的自引用?你考虑过吗?您是否考虑过两个具有相同但不同原型父级的对象?NaN
ownProperties
我只鼓励人们在练习并且代码不会投入生产时这样做。这是重新发明轮子的唯一理由。
- 优点:学习机会。
- 缺点:不可靠。需要时间和顾虑。
我父亲的比喻
这就像假设如果我父亲的名字是“约翰·史密斯”,他的生日是“1970 年 1 月 1 日”,那么任何名字是“约翰·史密斯”并且出生于“1970 年 1 月 1 日”的人都是我的父亲。
通常情况就是这样,但如果那天有两个“约翰·史密斯”出生呢?如果您认为您会考虑他们的身高,那么这提高了准确性,但仍然不是一个完美的比较。
2.1 你有限范围的DIY比较器
与其疯狂地以递归方式检查所有属性,不如考虑只检查“有限”数量的属性。例如,如果对象是 s,则可以比较它们的字段。User
emailAddress
它仍然不是一个完美的解决方案,但与解决方案#2相比,其好处是:
- 这是可以预测的,而且不太可能崩溃。
- 您正在推动相等的“定义”,而不是依赖于对象及其原型和嵌套属性的野生形式和形状。
3) 使用函数的库版本equal
如果您需要生产级质量,并且不能更改系统的设计,则很好。
例如,lodash 已经在 coolaj86 的答案中,或者 Tony Harvey 的答案或 Rafael Xavier 的 Node 中提到的 Angular 或 Ember 的答案中。_.equal
- 优点:这是其他人所做的。
- 缺点:外部依赖性,这可能会花费您额外的内存/CPU/安全问题,即使是一点点。此外,仍然可能遗漏一些边缘情况(例如,是否应将具有相同但不同原型父级的两个对象视为相同。最后,你可能会无意中用创可贴一个潜在的设计问题;只是说说而已!
ownProperties
我父亲的比喻
这就像付钱给中介公司,根据他的电话、姓名、地址等来寻找我的亲生父亲。
这将花费更多,而且可能比我自己进行背景调查更准确,但不包括边缘情况,例如我父亲是移民/庇护者,他的生日未知!
4) 在对象中使用标识符
如果你[仍然]可以改变系统的设计(你正在处理的对象),并且你希望你的代码持续很长时间,那就太好了。
它不适用于所有情况,并且可能不是很高性能。但是,如果您能做到,这是一个非常可靠的解决方案。
解决方案是,系统中的每个属性都将具有唯一标识符以及所有其他属性。标识符的唯一性将在生成时得到保证。在比较两个对象时,您将使用此 ID(也称为 UUID/GUID - 全局/通用唯一标识符)。即,当且仅当这些 ID 相等时,它们是相等的。object
ID 可以是简单的数字,也可以是通过库生成的字符串(建议)或一段代码。您需要做的就是确保它始终是唯一的,如果它可以内置,或者在UUID的情况下,可以检查所有现有值(例如MySQL的列属性)或简单地(如果来自库)是否依赖于冲突的可能性极低。auto_incremental
auto_incremental
UNIQUE
请注意,您还需要始终将 ID 与对象一起存储(以保证其唯一性),并且实时计算它可能不是最佳方法。
- 优点:可靠、高效、不脏、现代。
- 缺点:需要额外的空间。可能需要重新设计系统。
我父亲的比喻
就像知道我父亲的社会安全号码是 911-345-9283,所以任何拥有这个 SSN 的人都是我的父亲,任何自称是我父亲的人都必须拥有这个 SSN。
结论
我个人更喜欢解决方案#4(ID),而不是它们,因为它的准确性和可靠性。如果不可能,我会选择#2.1以获得可预测性,然后是#3。如果两者都不可能,则#2,最后是#1。
评论
o1 = { a: '1', b: '2' }
o2 = { b: '2', a: '1' }
JSON.stringify(o1) === JSON.stringify(o2) = false
下面是一个简短的实现,它使用但@Jor此处建议的键进行排序。JSON.stringify
一些测试是从这里@EbrahimByagowi的答案中获取的。
当然,通过使用 ,解决方案仅限于 JSON 可序列化类型(字符串、数字、JSON 对象、数组、布尔值、null)。不支持 、 等对象。JSON.stringify
Date
Function
function objectEquals(obj1, obj2) {
const JSONstringifyOrder = obj => {
const keys = {};
JSON.stringify(obj, (key, value) => {
keys[key] = null;
return value;
});
return JSON.stringify(obj, Object.keys(keys).sort());
};
return JSONstringifyOrder(obj1) === JSONstringifyOrder(obj2);
}
///////////////////////////////////////////////////////////////
/// 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("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}}));
let std1 = {
name: "Abhijeet",
roll: 1
}
let std2 = {
name: "Siddharth",
roll: 2
}
console.log(JSON.stringify(std1) === JSON.stringify(std2))
评论
JSON.stringify({y: 1, x: 1}) !== JSON.stringify({x: 1, y: 1})
我发现在忽略属性顺序的同时比较两个 javascript 对象的值的一种简单方法是使用 JSON stringify 替换器函数:
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);
这将在每一步对属性进行排序,以便字符串结果将不变于属性顺序。有些人以前可能做过这个,但我只是想我会分享它,以防万一:)。
字符串化两个对象并进行比较
return (JSON.stringify(obj1) === JSON.stringify(obj2))
这将返回 true 或 false
评论
a.hashCode() == b.hashCode()
a
b
person = { name: "fred", age: 42 }