Ensure _.isEqual works with maps and sets with circular references.

This commit is contained in:
John-David Dalton
2016-02-22 22:37:05 -08:00
parent d8da7a2142
commit af06529283
2 changed files with 63 additions and 13 deletions

View File

@@ -2895,8 +2895,9 @@
othIsObj = othTag == objectTag && !isHostObject(other),
isSameTag = objTag == othTag;
stack || (stack = new Stack);
if (isSameTag && !(objIsArr || objIsObj)) {
return equalByTag(object, other, objTag, equalFunc, customizer, bitmask);
return equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
}
var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
if (!isPartial) {
@@ -2910,7 +2911,6 @@
if (!isSameTag) {
return false;
}
stack || (stack = new Stack);
return (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, bitmask, stack);
}
@@ -4704,9 +4704,9 @@
* @param {Array} array The array to compare.
* @param {Array} other The other array to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparisons.
* @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Object} [stack] Tracks traversed `array` and `other` objects.
* @param {Function} customizer The function to customize comparisons.
* @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Object} stack Tracks traversed `array` and `other` objects.
* @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
*/
function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
@@ -4773,11 +4773,12 @@
* @param {Object} other The other object to compare.
* @param {string} tag The `toStringTag` of the objects to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparisons.
* @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Function} customizer The function to customize comparisons.
* @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Object} stack Tracks traversed `object` and `other` objects.
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
function equalByTag(object, other, tag, equalFunc, customizer, bitmask) {
function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
switch (tag) {
case arrayBufferTag:
if ((object.byteLength != other.byteLength) ||
@@ -4812,9 +4813,18 @@
var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
convert || (convert = setToArray);
if (object.size != other.size && !isPartial) {
return false;
}
// Assume cyclic values are equal.
var stacked = stack.get(object);
if (stacked) {
return stacked == other;
}
stack.set(object, other);
// Recursively compare objects (susceptible to call stack limits).
return (isPartial || object.size == other.size) &&
equalFunc(convert(object), convert(other), customizer, bitmask | UNORDERED_COMPARE_FLAG);
return equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask | UNORDERED_COMPARE_FLAG, stack);
case symbolTag:
return !!Symbol && (symbolValueOf.call(object) == symbolValueOf.call(other));
@@ -4830,9 +4840,9 @@
* @param {Object} object The object to compare.
* @param {Object} other The other object to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparisons.
* @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Object} [stack] Tracks traversed `object` and `other` objects.
* @param {Function} customizer The function to customize comparisons.
* @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` for more details.
* @param {Object} stack Tracks traversed `object` and `other` objects.
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {

View File

@@ -9090,6 +9090,26 @@
}
});
QUnit.test('should compare maps with circular references', function(assert) {
assert.expect(2);
if (Map) {
var map1 = new Map,
map2 = new Map;
map1.set('a', map1);
map2.set('a', map2);
assert.strictEqual(_.isEqual(map1, map2), true);
map1.set('b', 1);
map2.set('b', 2);
assert.strictEqual(_.isEqual(map1, map2), false);
}
else {
skipAssert(assert, 2);
}
});
QUnit.test('should compare regexes', function(assert) {
assert.expect(5);
@@ -9127,6 +9147,26 @@
}
});
QUnit.test('should compare sets with circular references', function(assert) {
assert.expect(2);
if (Set) {
var set1 = new Set,
set2 = new Set;
set1.add(set1);
set2.add(set2);
assert.strictEqual(_.isEqual(set1, set2), true);
set1.add(1);
set2.add(2);
assert.strictEqual(_.isEqual(set1, set2), false);
}
else {
skipAssert(assert, 2);
}
});
QUnit.test('should compare typed arrays', function(assert) {
assert.expect(1);