【问题标题】:JavaScript: Deep Copy Circular JSONJavaScript:深拷贝循环 JSON
【发布时间】:2015-05-05 23:37:33
【问题描述】:

简介:

我正在尝试编写深拷贝方法,但需要跟踪我访问过的节点,以便我可以链接到以前的visitedNode,而不是永远深拷贝直到堆栈溢出。

尝试:

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

我没有一种独特的方式来存储内存位置——它将自己存储在[object Object],我不能使用 JSON.stringify,因为它是一个循环结构


我尝试使用var visitedNodes = new Map();,但仍然没有骰子


我目前的方法是使用Array.prototype.indexOf 函数,但我不知道它是否也适用于循环结构,因为我在这里也遇到堆栈溢出!!!

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
};

问题:

任何人有任何想法来获得一个唯一的内存地址,这样我就可以确定我以前是否去过对象的引用?我相信我可以基于 Object.keys()Object.prototype.constructor 构造一个唯一的哈希,但这似乎很荒谬,如果构造函数相同并且子键与父键相同,则会给出误报

【问题讨论】:

标签: javascript json clone deep-copy


【解决方案1】:

在visitedNodes中保存原始引用,并创建另一个数组以相同索引保存克隆对象,以便在引用时使用。

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);

代码运行示例: http://jsbin.com/favekexiba/1/watch?js,console

【讨论】:

  • 我相信我在做同样的事情,只是使用短路运算符来递归地传递visitedNodes 引用而不是使用辅助函数
  • 这应该可行,我认为主要问题与我们特定的 JS 核心有关
  • 不,我们没有做同样的事情...您的代码几乎就在那里,但是...({} === {}) === false 对象具有相同的属性,但有 2 个不同的对象
  • 而不是这个:visitedNodes.push(cloneObject); 你应该这样做:visitedNodes.push(item);
  • 剩下的唯一步骤是确定function.bind(visitedNode) 上下文
【解决方案2】:

Fetz 答案中的代码效果很好,但在 Date 对象上会中断。这是一个补丁版本:

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);

我更愿意编辑 Fetz 的答案,但编辑队列已满。

edit 19/07/2017:还添加了 XML 文档克隆

【讨论】:

    猜你喜欢
    • 2014-01-06
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2011-09-05
    • 1970-01-01
    相关资源
    最近更新 更多