JavaScript: Deep Copy Circular JSON

自作多情 提交于 2020-01-14 20:16:35

问题


intro:

I'm trying to write a deep copy method, but need to keep track of my visited nodes, so that I can link to the previously visitedNode instead of deep copying forever until stack overflow.

attempts:

var visitedNodes = {};
var obj = {}; obj.a = obj;   // circular; can't use JSON.stringify)
var obj2 = {};

visitedNodes[obj] = "should need key obj (not obj2) to access this string";

console.log(visitedNodes[obj2]);    // logs the string unfortunately

I don't have a unique way of storing the memory location -- it stores itself at [object Object] and I can't use JSON.stringify because it's a circular structure


I tried using var visitedNodes = new Map(); but still no dice


My current approach is to utilize the Array.prototype.indexOf function, but I don't know if it works with circular structures either, because I'm getting a stack overflow here too!!!

this.clone = function (item, visitedNodes) {
    visitedNodes = visitedNodes || [];
    if (typeof item === "object" && !Array.isArray(item)) {
        if (visitedNodes.indexOf(item) === -1) {
            var cloneObject = {};
            visitedNodes.push(cloneObject);
            for (var i in item) {
                if (item.hasOwnProperty(i)) {
                    cloneObject[i] = this.clone(item[i], visitedNodes);
                }
            }
            return cloneObject;
        } else {
            return visitedNodes[visitedNodes.indexOf(item)];
        }
    }
    else if (typeof item === "object" && Array.isArray(item)) {
        if (visitedNodes.indexOf(item) === -1) {
            var cloneArray = [];
            visitedNodes.push(cloneArray);
            for (var j = 0; j < item.length; j++) {
                cloneArray.push(this.clone(item[j], visitedNodes));
            }
            return cloneArray;
        } else {
            return visitedNodes[visitedNodes.indexOf(item)];
        }
    }

    return item; // not object, not array, therefore primitive
};

the question:

Anyone have any ideas for getting a unique memory address so I can determine if I've been to the Object's reference before? I believe I could construct a unique hash based off Object.keys() and Object.prototype.constructor but that seems absurd and will give false positives if constructor is the same and the child keys are the same as the parent


回答1:


In visitedNodes save the original reference, and create another array to save with same index the clone object to use when is a reference.

function deepClone(obj) {
    var visitedNodes = [];
    var clonedCopy = [];
    function clone(item) {
        if (typeof item === "object" && !Array.isArray(item)) {
            if (visitedNodes.indexOf(item) === -1) {
                visitedNodes.push(item);
                var cloneObject = {};
                clonedCopy.push(cloneObject);
                for (var i in item) {
                    if (item.hasOwnProperty(i)) {
                        cloneObject[i] = clone(item[i]);
                    }
                }
                return cloneObject;
            } else {
                return clonedCopy[visitedNodes.indexOf(item)];
            }
        }
        else if (typeof item === "object" && Array.isArray(item)) {
            if (visitedNodes.indexOf(item) === -1) {
                var cloneArray = [];
                visitedNodes.push(item);
                clonedCopy.push(cloneArray);
                for (var j = 0; j < item.length; j++) {
                    cloneArray.push(clone(item[j]));
                }
                return cloneArray;
            } else {
                return clonedCopy[visitedNodes.indexOf(item)];
            }
        }

        return item; // not object, not array, therefore primitive
    }
    return clone(obj);
}

var obj = {b: 'hello'};
obj.a = { c: obj };
var dolly = deepClone(obj);
obj.d = 'hello2';
console.log(obj);
console.log(dolly);

the code running example: http://jsbin.com/favekexiba/1/watch?js,console




回答2:


The code in the answer of Fetz works great but breaks on Date objects. Here is a patched version:

const visitedNodes = [];
const clonedCopy = [];

function clone(item) {
  if (typeof item === 'object') {
    if (item instanceof Date) { // Date
      if (visitedNodes.indexOf(item) === -1) {
        visitedNodes.push(item);
        var cloneObject = new Date(item);
        clonedCopy.push(cloneObject);
        return cloneObject;
      }
      return clonedCopy[visitedNodes.indexOf(item)];
    } else if (XMLDocument && item instanceof XMLDocument) { // XML Document
      if (visitedNodes.indexOf(item) === -1) {
        visitedNodes.push(item);
        const cloneObject = item.implementation.createDocument(item.documentElement.namespaceURI, null, null);
        const newNode = cloneObject.importNode(item.documentElement, true);
        cloneObject.appendChild(newNode);
        clonedCopy.push(cloneObject);
        return cloneObject;
      }
      return clonedCopy[visitedNodes.indexOf(item)];
    } else if (!Array.isArray(item)) { // Object
      if (visitedNodes.indexOf(item) === -1) {
        visitedNodes.push(item);
        var cloneObject = {};
        clonedCopy.push(cloneObject);
        for (const i in item) {
          if (item.hasOwnProperty(i)) {
            cloneObject[i] = clone(item[i]);
          }
        }
        return cloneObject;
      }
      return clonedCopy[visitedNodes.indexOf(item)];
    } else if (Array.isArray(item)) { // Array
      if (visitedNodes.indexOf(item) === -1) {
        const cloneArray = [];
        visitedNodes.push(item);
        clonedCopy.push(cloneArray);
        for (let j = 0; j < item.length; j++) {
          cloneArray.push(clone(item[j]));
        }
        return cloneArray;
      }
      return clonedCopy[visitedNodes.indexOf(item)];
    }
  }

  return item; // not date, not object, not array, therefore primitive
}
return clone(obj);

I would've preferred to edit the answer of Fetz but the edit queue is full.

edit 19/07/2017: Added XML Document cloning as well



来源:https://stackoverflow.com/questions/30064997/javascript-deep-copy-circular-json

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!