这是我的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、未定义或 0。
出于效率考虑,我尽快比较了属性的相等性。然后,仅当失败时,才查找这些属性的类型。对于具有大量标量属性的大型对象,速度提升可能很重要。
不再需要两个循环,第一个循环从左侧对象检查属性,第二个循环从右侧检查属性并仅验证是否存在(而不是值),以捕获这些使用未定义值定义的属性。
总体而言,此代码仅用 16 行代码即可处理大多数极端情况(无注释)。
更新 (8/13/2015).我已经实现了一个更好的版本,因为函数value_equals()更快,可以正确处理诸如NaN和0等与-0不同的角落情况,可以选择强制执行对象的属性顺序并测试循环引用,并由100多个自动测试作为Toubkal项目测试套件的一部分提供支持。