Issue #329 -- significant change to _.isEqual semantics.

This commit is contained in:
Jeremy Ashkenas
2011-10-18 16:07:23 -04:00
parent 6123870dab
commit e79586515c
2 changed files with 21 additions and 32 deletions

View File

@@ -154,8 +154,8 @@ $(document).ready(function() {
ok(_.isEqual({}, {}), "Empty object literals are equal"); ok(_.isEqual({}, {}), "Empty object literals are equal");
ok(_.isEqual([], []), "Empty array literals are equal"); ok(_.isEqual([], []), "Empty array literals are equal");
ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects 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}, []), "Array-like objects and arrays are not equal.");
ok(_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects");
ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); ok(!_.isEqual({}, []), "Object literals and array literals are not equal");
ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); 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; b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null;
// Array elements and properties. // 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"); a.push("White Rocks");
ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); ok(!_.isEqual(a, b), "Arrays of different lengths are not equal");
a.push("East Boulder"); a.push("East Boulder");
@@ -183,7 +183,7 @@ $(document).ready(function() {
// Sparse arrays. // Sparse arrays.
ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); 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` // 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` // elements in arrays as elisions. Thus, sparse arrays and dense arrays containing `undefined`
@@ -235,8 +235,8 @@ $(document).ready(function() {
// Instances. // Instances.
ok(_.isEqual(new First, new First), "Object instances are equal"); 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(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 identical"); 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"); ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined");
// Circular Arrays. // Circular Arrays.

View File

@@ -673,6 +673,8 @@
} }
// Ensure that both values are objects. // Ensure that both values are objects.
if (typeA != 'object') return false; 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 // Assume equality for cyclic structures. The algorithm for detecting cyclic structures is
// adapted from ES 5.1 section 15.12.3, abstract operation `JO`. // adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length; var length = stack.length;
@@ -684,34 +686,21 @@
// Add the first object to the stack of traversed objects. // Add the first object to the stack of traversed objects.
stack.push(a); stack.push(a);
var size = 0, result = true; var size = 0, result = true;
if (a.length === +a.length || b.length === +b.length) { // Deep compare objects.
// Compare object lengths to determine if a deep comparison is necessary. for (var key in a) {
size = a.length; if (hasOwnProperty.call(a, key)) {
result = size == b.length; // Count the expected number of properties.
if (result) { size++;
// Deep compare array-like object contents, ignoring non-numeric properties. // Deep compare each member.
while (size--) { if (!(result = hasOwnProperty.call(b, key) && eq(a[key], b[key], stack))) break;
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
}
} }
} else { }
// Deep compare objects. // Ensure that both objects contain the same number of properties.
for (var key in a) { if (result) {
if (hasOwnProperty.call(a, key)) { for (key in b) {
// Count the expected number of properties. if (hasOwnProperty.call(b, key) && !size--) break;
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;
} }
result = !size;
} }
// Remove the first object from the stack of traversed objects. // Remove the first object from the stack of traversed objects.
stack.pop(); stack.pop();