Skip to content

Commit daaed2e

Browse files
committed
fix(equals): handle objects with circular references
Make `angular.equals` handle circular references. Closes angular#11372
1 parent 992114f commit daaed2e

File tree

2 files changed

+22
-5
lines changed

2 files changed

+22
-5
lines changed

src/Angular.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,12 @@ function shallowCopy(src, dst) {
859859

860860
return dst || src;
861861
}
862-
862+
function equalsCacheContains(cache, o1, o2) {
863+
for (var i = 0, ii = cache.length; i < ii; i += 2) {
864+
if (cache[i] === o1 && cache[i + 1] === o2) return true;
865+
}
866+
return false;
867+
}
863868

864869
/**
865870
* @ngdoc function
@@ -890,24 +895,27 @@ function shallowCopy(src, dst) {
890895
* @param {*} o2 Object or value to compare.
891896
* @returns {boolean} True if arguments are equal.
892897
*/
893-
function equals(o1, o2) {
898+
function equals(o1, o2, cache) {
894899
if (o1 === o2) return true;
895900
if (o1 === null || o2 === null) return false;
896901
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
902+
cache = cache || [];
903+
if (equalsCacheContains(cache, o1, o2)) return true;
904+
cache.push(o1, o2);
897905
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
898906
if (t1 == t2) {
899907
if (t1 == 'object') {
900908
if (isArray(o1)) {
901909
if (!isArray(o2)) return false;
902910
if ((length = o1.length) == o2.length) {
903911
for (key = 0; key < length; key++) {
904-
if (!equals(o1[key], o2[key])) return false;
912+
if (!equals(o1[key], o2[key], cache)) return false;
905913
}
906914
return true;
907915
}
908916
} else if (isDate(o1)) {
909917
if (!isDate(o2)) return false;
910-
return equals(o1.getTime(), o2.getTime());
918+
return equals(o1.getTime(), o2.getTime(), cache);
911919
} else if (isRegExp(o1)) {
912920
return isRegExp(o2) ? o1.toString() == o2.toString() : false;
913921
} else {
@@ -916,7 +924,7 @@ function equals(o1, o2) {
916924
keySet = {};
917925
for (key in o1) {
918926
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
919-
if (!equals(o1[key], o2[key])) return false;
927+
if (!equals(o1[key], o2[key], cache)) return false;
920928
keySet[key] = true;
921929
}
922930
for (key in o2) {

test/AngularSpec.js

+9
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,15 @@ describe('angular', function() {
651651
it('should return false when comparing an object and a Date', function() {
652652
expect(equals({}, new Date())).toBe(false);
653653
});
654+
655+
it('should handle objects with circular references', function() {
656+
var elem1, elem2;
657+
elem1 = {};
658+
elem1.ref = elem1;
659+
elem2 = {};
660+
elem2.ref = elem2;
661+
expect(equals(elem1, elem2)).toBe(true);
662+
});
654663
});
655664

656665

0 commit comments

Comments
 (0)