diff --git a/lodash.js b/lodash.js index ddadd181d..bb1476f37 100644 --- a/lodash.js +++ b/lodash.js @@ -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) { diff --git a/test/test.js b/test/test.js index 54673f4eb..f0b0f595f 100644 --- a/test/test.js +++ b/test/test.js @@ -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);