严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?
堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。
当前回答
我对这个函数做如下假设:
你控制你比较的对象,你只有基本的值(例如。而不是嵌套的对象、函数等)。 您的浏览器支持Object.keys。
这应该被视为一个简单策略的示范。
/**
* 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;
}
其他回答
在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
简短的回答
简单的答案是:不,没有一般的方法来确定一个对象等于另一个你所指的意义。例外情况是当您严格地认为一个对象是无类型的。
长话短说
这个概念是一个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可能在不同实例之间有所不同,但在比较中并不显著。
注意,当成员本身也可能是某个类型的实例,并且每个实例都需要有确定相等的方法时,这个问题就会出现。
更复杂的是,在JavaScript中,数据和方法之间的区别是模糊的。
一个对象可以引用一个作为事件处理程序调用的方法,这可能不被认为是其“值状态”的一部分。然而,另一个对象很可能被分配一个执行重要计算的函数,从而使这个实例与其他实例不同,仅仅因为它引用了不同的函数。
如果一个对象的现有原型方法被另一个函数覆盖,该怎么办?它还能被认为与另一个相同的实例相等吗?这个问题只能在每种类型的具体情况下回答。
如前所述,异常将是一个严格的无类型对象。在这种情况下,唯一明智的选择是对每个成员进行迭代和递归比较。即使这样,人们也要问一个函数的“值”是什么?
我建议不要使用散列或序列化(正如JSON解决方案所建议的那样)。如果需要测试两个对象是否相等,则需要定义相等的含义。这可能是两个对象中的所有数据成员都匹配,也可能是内存位置必须匹配(意味着两个变量在内存中引用同一个对象),或者每个对象中只有一个数据成员必须匹配。
最近我开发了一个对象,它的构造函数在每次创建实例时都会创建一个新的id(从1开始,加1)。该对象有一个isEqual函数,用于将该id值与另一个对象的id值进行比较,如果匹配则返回true。
在这种情况下,我定义“相等”的意思是id值匹配。假设每个实例都有一个唯一的id,这可以用来加强匹配对象也占用相同内存位置的想法。尽管这是不必要的。
如果使用JSON库,可以将每个对象编码为JSON,然后比较结果字符串是否相等。
var obj1={test:"value"};
var obj2={test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
注意:虽然这个答案在很多情况下都有效,但由于各种原因,一些人在评论中指出了它的问题。在几乎所有情况下,您都希望找到更健壮的解决方案。
你是在测试两个对象是否相等吗?即:它们的属性是相等的?
如果是这样的话,你可能会注意到这种情况:
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()函数。