Detecting and fixing circular references in JavaScript

后端 未结 15 822
庸人自扰
庸人自扰 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-29 00:10

    @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
    

提交回复
热议问题