Detecting and fixing circular references in JavaScript

后端 未结 15 811
庸人自扰
庸人自扰 2020-11-28 23:29

Given I have a circular reference in a large JavaScript object

And I try JSON.stringify(problematicObject)

And the browser throws

15条回答
  •  独厮守ぢ
    2020-11-28 23:58

    There's a lot of answers here, but I thought I'd add my solution to the mix. It's similar to @Trey Mack's answer, but that solution takes O(n^2). This version uses WeakMap instead of an array, improving the time to O(n).

    function isCyclic(object) {
       const seenObjects = new WeakMap(); // use to keep track of which objects have been seen.
    
       function detectCycle(obj) {
          // If 'obj' is an actual object (i.e., has the form of '{}'), check
          // if it's been seen already.
          if (Object.prototype.toString.call(obj) == '[object Object]') {
    
             if (seenObjects.has(obj)) {
                return true;
             }
    
             // If 'obj' hasn't been seen, add it to 'seenObjects'.
             // Since 'obj' is used as a key, the value of 'seenObjects[obj]'
             // is irrelevent and can be set as literally anything you want. I 
             // just went with 'undefined'.
             seenObjects.set(obj, undefined);
    
             // Recurse through the object, looking for more circular references.
             for (var key in obj) {
                if (detectCycle(obj[key])) {
                   return true;
                }
             }
    
          // If 'obj' is an array, check if any of it's elements are
          // an object that has been seen already.
          } else if (Array.isArray(obj)) {
             for (var i in obj) {
                if (detectCycle(obj[i])) {
                   return true;
                }
             }
          }
    
          return false;
       }
    
       return detectCycle(object);
    }
    

    And this is what it looks like in action.

    > var foo = {grault: {}};
    > detectCycle(foo);
    false
    > foo.grault = foo;
    > detectCycle(foo);
    true
    > var bar = {};
    > detectCycle(bar);
    false
    > bar.plugh = [];
    > bar.plugh.push(bar);
    > detectCycle(bar);
    true
    

提交回复
热议问题