如何确定两个JavaScript对象的相等性?
严格相等运算符将告诉您两个对象类型是否相等。但是,有没有一种方法可以判断两个对象是否相等,就像Java中的哈希代码值一样如何确定两个JavaScript对象的相等性?,javascript,object,equals,hashcode,Javascript,Object,Equals,Hashcode,严格相等运算符将告诉您两个对象类型是否相等。但是,有没有一种方法可以判断两个对象是否相等,就像Java中的哈希代码值一样 堆栈溢出问题类似于此问题,但需要更具学术性的答案。上面的场景演示了为什么必须有一个,我想知道是否有任何等价的解决方案您是否正在尝试测试两个对象是否相等?ie:它们的属性是相等的吗 如果是这种情况,您可能已经注意到这种情况: var a = { foo : "bar" }; var b = { foo : "bar" }; alert (a == b ? "Equal" : "
堆栈溢出问题类似于此问题,但需要更具学术性的答案。上面的场景演示了为什么必须有一个,我想知道是否有任何等价的解决方案您是否正在尝试测试两个对象是否相等?ie:它们的属性是相等的吗 如果是这种情况,您可能已经注意到这种情况:
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;
}
({a: 1, b: "h"}).equals({a: 1, b: "h"});
显然,这个函数需要进行大量的优化,并且能够进行深度检查(处理嵌套对象:var a={foo:{fu:{fu:{bar}}
),但是您知道了
正如所指出的,您可能需要根据自己的目的对其进行调整,例如:不同的类可能对“equal”有不同的定义。如果您只是在处理普通对象,以上内容可能就足够了,否则可以使用自定义的
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;
}
}
在上述情况下,c
对于确定MyClass的任何两个实例是否相等并不重要,只有a
和b
才重要。在某些情况下,c
可能因实例而异,但在比较过程中并不显著
注:当成员本身也可能是某一类型的实例时,此问题适用,并且每个成员都需要有确定平等性的方法
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
更复杂的是,在JavaScript中,数据和方法之间的区别是模糊的
一个对象可能引用一个将作为事件处理程序调用的方法,而这可能不会被视为其“值状态”的一部分。而另一个对象很可能被指定一个函数,该函数执行一个重要的计算,从而使该实例与其他实例不同,因为它引用了不同的函数
如果一个对象的一个现有原型方法被另一个函数覆盖,那么该对象会怎么样?它是否仍然可以被视为等同于另一个在其他方面相同的实例?这个问题只能在每种类型的具体情况下得到回答
如前所述,例外情况将是一个严格的无类型对象。在这种情况下,唯一明智的选择是对每个成员进行迭代和递归比较。即使这样,人们也必须问函数的“值”是什么?我建议不要使用哈希或序列化(正如JSON解决方案所建议的那样)。如果需要测试两个对象是否相等,则需要定义相等的含义。可能是两个对象中的所有数据成员都匹配,也可能是内存位置必须匹配(这意味着两个变量都引用内存中的同一对象),也可能是每个对象中只有一个数据成员必须匹配 最近,我开发了一个对象,其构造函数在每次创建实例时创建一个新id(从1开始,递增1)。此对象具有一个isEqual函数,该函数将该id值与另一个对象的id值进行比较,如果匹配,则返回true
在这种情况下,我将“equal”定义为id值匹配的意思。假设每个实例都有一个唯一的id,这可以用来强化匹配对象也占用相同内存位置的想法。尽管这不是必需的。当对象引用内存中的同一位置时,JavaScript中对象的默认相等运算符将产生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
由于需要一个比之前发布的更通用的对象比较函数,我编写了以下代码。评论欣赏
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;
}
为什么要重新发明轮子?试试看。它具有许多必备功能,例如 它将强制检查每个键值-就像本页上的其他示例一样-使用和本机优化(如果它们在浏览器中可用)
注意:以前这个答案是推荐的,但是已经做了更好的工作,解决了bug和一致性问题。
如果两个对象都有相同的值,对于所有的属性和递归的所有嵌套对象和数组都是很有用的。我还认为以下两个对象是相等的:
var a = {p1: 1};
var b = {p1: 1, p2: undefined};
类似地,数组可以有“缺失”元素和未定义的元素。我也会同样对待他们:
var c = [1, 2];
var d = [1, 2, undefined];
实现此相等定义的函数:
function isEqual(a, b) {
if (a === b) {
return true;
}
if (generalType(a) != generalType(b)) {
return false;
}
if (a == b) {
return true;
}
if (typeof a != 'object') {
return false;
}
// null != {}
if (a instanceof Object != b instanceof Object) {
return false;
}
if (a instanceof Date || b instanceof Date) {
if (a instanceof Date != b instanceof Date ||
a.getTime() != b.getTime()) {
return false;
}
}
var allKeys = [].concat(keys(a), keys(b));
uniqueArray(allKeys);
for (var i = 0; i < allKeys.length; i++) {
var prop = allKeys[i];
if (!isEqual(a[prop], b[prop])) {
return false;
}
}
return true;
}
函数相等(a,b){
如果(a==b){
返回tru
/**
* Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
* @param {Object} object1
* @param {Object} object2
* @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
* @returns {Boolean}
*/
function isEqual( object1, object2, order_matters ) {
var keys1 = Object.keys(object1),
keys2 = Object.keys(object2),
i, key;
// Test 1: Same number of elements
if( keys1.length != keys2.length ) {
return false;
}
// If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
// keys1 = Object.keys({a:2, b:1}) = ["a","b"];
// keys2 = Object.keys({b:1, a:2}) = ["b","a"];
// This is why we are sorting keys1 and keys2.
if( !order_matters ) {
keys1.sort();
keys2.sort();
}
// Test 2: Same keys
for( i = 0; i < keys1.length; i++ ) {
if( keys1[i] != keys2[i] ) {
return false;
}
}
// Test 3: Values
for( i = 0; i < keys1.length; i++ ) {
key = keys1[i];
if( object1[key] != object2[key] ) {
return false;
}
}
return true;
}
var shallowCompareObjects = function(o1, o2, bCompareValues) {
var s,
n1 = 0,
n2 = 0,
b = true;
for (s in o1) { n1 ++; }
for (s in o2) {
if (!o1.hasOwnProperty(s)) {
b = false;
break;
}
if (bCompareValues && o1[s] !== o2[s]) {
b = false;
break;
}
n2 ++;
}
return b && n1 == n2;
}
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 } }
var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError
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;
};
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(''));
}
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;
});
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;
}
}
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);
}
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);
}
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;
}
({a: 1, b: "h"}).equals({a: 1, b: "h"});
({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"]})
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;
}
var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true
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])