以下是我的ES3注释解决方案(代码后的血腥细节):
function object_equals( x, y ) {
if ( x === y ) return true;
// if both x and y are null or undefined and exactly the same
if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;
// if they are not strictly equal, they both need to be Objects
if ( x.constructor !== y.constructor ) return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
for ( var p in x ) {
if ( ! x.hasOwnProperty( p ) ) continue;
// other properties were tested using x.constructor === y.constructor
if ( ! y.hasOwnProperty( p ) ) return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if ( x[ p ] === y[ p ] ) continue;
// if they have the same strict value or identity then they are equal
if ( typeof( x[ p ] ) !== "object" ) return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if ( ! object_equals( x[ p ], y[ p ] ) ) return false;
// Objects and Arrays must be tested recursively
}
for ( p in y )
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) )
return false;
// allows x[ p ] to be set to undefined
return true;
}
在开发这个解决方案的过程中,我特别关注了角落的情况,效率,但试图产生一个简单的解决方案,希望能有一些优雅。JavaScript允许空的和未定义的财产,对象具有原型链,如果不进行检查,可能会导致非常不同的行为。
首先,我选择不扩展Object.prototype,主要是因为null不能作为比较对象之一,并且我认为null应该是一个有效的对象,可以与其他对象进行比较。其他人也注意到了Object.prototype的扩展对其他代码可能产生的副作用。
必须特别注意处理JavaScript允许对象财产设置为未定义的可能性,即存在值设置为未确定的财产。上述解决方案验证了这两个对象是否具有相同的设置为未定义的财产以报告相等性。这只能通过使用Object.hasOwnProperty(property_name)检查财产是否存在来完成。还要注意,JSON.stringify()删除了设置为未定义的财产,因此使用此表单进行比较时将忽略设置为未确定值的财产。
只有当函数共享相同的引用,而不仅仅是相同的代码时,才应该认为它们是相等的,因为这不会考虑这些函数原型。因此,比较代码字符串不能保证它们具有相同的原型对象。
这两个对象应该具有相同的原型链,而不仅仅是相同的财产。这只能通过比较两个对象的构造函数以确保严格相等来跨浏览器测试。ECMAScript 5将允许使用Object.getPrototypeOf()测试他们的实际原型。一些web浏览器还提供了__proto__属性,该属性可以执行相同的操作。对上述代码的一个可能的改进将允许在任何可用的情况下使用这些方法之一。
这里使用严格比较是最重要的,因为2不应被视为等于“2.0000”,false也不应被认为等于null、undefined或0。
出于效率考虑,我会尽快比较财产的相等性。然后,只有在这失败的情况下,才能查找这些财产的类型。速度提升对于具有大量标量财产的大型对象来说意义重大。
不再需要两个循环,第一个循环从左侧对象检查财产,第二个循环从右侧检查财产并仅验证存在性(而不是值),以捕获使用未定义值定义的这些财产。
总的来说,这段代码只需要16行代码(没有注释)就可以处理大部分的角情况。
更新(2015年8月13日)。我实现了一个更好的版本,因为函数value_equals()速度更快,能够正确地处理诸如NaN和0不同于-0的拐角情况,可以选择强制对象的财产顺序并测试循环引用,作为Toubkal项目测试套件的一部分,它由100多个自动测试支持。