From e79586515c5f635650afc32976826e01463dcd1d Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 18 Oct 2011 16:07:23 -0400 Subject: [PATCH] Issue #329 -- significant change to _.isEqual semantics. --- test/objects.js | 12 ++++++------ underscore.js | 41 +++++++++++++++-------------------------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/test/objects.js b/test/objects.js index c131d920b..5fc4b6a0a 100644 --- a/test/objects.js +++ b/test/objects.js @@ -154,8 +154,8 @@ $(document).ready(function() { ok(_.isEqual({}, {}), "Empty object literals are equal"); ok(_.isEqual([], []), "Empty array literals are equal"); ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects are equal"); - ok(_.isEqual({length: 0}, []), "Array-like objects and arrays are equal"); - ok(_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); + ok(!_.isEqual({length: 0}, []), "Array-like objects and arrays are not equal."); + ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); @@ -174,7 +174,7 @@ $(document).ready(function() { b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; // Array elements and properties. - ok(_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are equal"); + ok(!_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are not equal"); a.push("White Rocks"); ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); a.push("East Boulder"); @@ -183,7 +183,7 @@ $(document).ready(function() { // Sparse arrays. ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); - ok(!_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are not equal"); + ok(_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are equal when both are empty"); // According to the Microsoft deviations spec, section 2.1.26, JScript 5.x treats `undefined` // elements in arrays as elisions. Thus, sparse arrays and dense arrays containing `undefined` @@ -235,8 +235,8 @@ $(document).ready(function() { // Instances. ok(_.isEqual(new First, new First), "Object instances are equal"); - ok(_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are equal"); - ok(_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are identical"); + ok(!_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are not equal"); + ok(!_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are not identical"); ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined"); // Circular Arrays. diff --git a/underscore.js b/underscore.js index d19e26960..5e9d4f3e2 100644 --- a/underscore.js +++ b/underscore.js @@ -673,6 +673,8 @@ } // Ensure that both values are objects. if (typeA != 'object') return false; + // Objects with different constructors are not equal. + if (a.constructor !== b.constructor) return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic structures is // adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = stack.length; @@ -684,34 +686,21 @@ // Add the first object to the stack of traversed objects. stack.push(a); var size = 0, result = true; - if (a.length === +a.length || b.length === +b.length) { - // Compare object lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare array-like object contents, ignoring non-numeric properties. - while (size--) { - // Ensure commutative equality for sparse arrays. - if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break; - } + // Deep compare objects. + for (var key in a) { + if (hasOwnProperty.call(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break; } - } else { - // Deep compare objects. - for (var key in a) { - if (hasOwnProperty.call(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (hasOwnProperty.call(b, key) && !size--) break; - } - result = !size; + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (hasOwnProperty.call(b, key) && !size--) break; } + result = !size; } // Remove the first object from the stack of traversed objects. stack.pop();