Given I have a circular reference in a large JavaScript object
And I try JSON.stringify(problematicObject)
And the browser throws
"TypeError: Converting circular structure to JSON"
(which is expected)
Then I want to find the cause of this circular reference, preferably using Chrome developer tools? Is this possible? How do you find and fix circular references in a large object?
A circular reference occurs if two separate objects pass references to each other. In older browsers circular references were a cause of memory leaks. With improvements in Garbage collection algorithms, which can now handle cycles and cyclic dependencies fine, this is no longer an issue.
However, from a pure design point of view, circular referencing is still a bad thing and a code smell. Circular referencing implies that the 2 objects referencing each other are tightly coupled and changes to one object may need changes in other as well. There is no one way to avoid circular reference in JS.
Circular reference occurs when two or more interdependent resources cause lock condition. This makes the resource unusable. To handle the problem of circular references in C#, you should use garbage collection. It detects and collects circular references.
The circular reference error message "There are one or more circular references where a formula refers to its own cell either directly or indirectly. This might cause them to calculate incorrectly. Try removing or changing these references, or moving the formulas to different cells."
Pulled from http://blog.vjeux.com/2011/javascript/cyclic-object-detection.html. One line added to detect where the cycle is. Paste this into the Chrome dev tools:
function isCyclic (obj) { var seenObjects = []; function detect (obj) { if (obj && typeof obj === 'object') { if (seenObjects.indexOf(obj) !== -1) { return true; } seenObjects.push(obj); for (var key in obj) { if (obj.hasOwnProperty(key) && detect(obj[key])) { console.log(obj, 'cycle at ' + key); return true; } } } return false; } return detect(obj); }
Here's the test:
> a = {} > b = {} > a.b = b; b.a = a; > isCyclic(a) Object {a: Object} "cycle at a" Object {b: Object} "cycle at b" true
@tmack's answer is definitely what I was looking for when I found this question!
Unfortunately it returns many false positives - it returns true if an object is replicated in the JSON, which isn't the same as circularity. Circularity means that an object is its own child, e.g.
obj.key1.key2.[...].keyX === obj
I modified the original answer, and this is working for me:
function isCyclic(obj) { var keys = []; var stack = []; var stackSet = new Set(); var detected = false; function detect(obj, key) { if (obj && typeof obj != 'object') { return; } if (stackSet.has(obj)) { // it's cyclic! Print the object and its locations. var oldindex = stack.indexOf(obj); var l1 = keys.join('.') + '.' + key; var l2 = keys.slice(0, oldindex + 1).join('.'); console.log('CIRCULAR: ' + l1 + ' = ' + l2 + ' = ' + obj); console.log(obj); detected = true; return; } keys.push(key); stack.push(obj); stackSet.add(obj); for (var k in obj) { //dive on the object's children if (Object.prototype.hasOwnProperty.call(obj, k)) { detect(obj[k], k); } } keys.pop(); stack.pop(); stackSet.delete(obj); return; } detect(obj, 'obj'); return detected; }
Here are a few very simple tests:
var root = {} var leaf = {'isleaf':true}; var cycle2 = {l:leaf}; var cycle1 = {c2: cycle2, l:leaf}; cycle2.c1 = cycle1 root.leaf = leaf isCyclic(cycle1); // returns true, logs "CIRCULAR: obj.c2.c1 = obj" isCyclic(cycle2); // returns true, logs "CIRCULAR: obj.c1.c2 = obj" isCyclic(leaf); // returns false isCyclic(root); // returns false
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With