我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。

如何正确克隆JavaScript对象?


当前回答

A.Levy的答案几乎是完整的,这是我的一点贡献:有一种方法可以处理递归引用,请看下面这行

如果(this[attr]==this)copy[attr]=复制;

如果对象是XMLDOM元素,则必须改用cloneNode

if(this.cloneNode)返回this.clone节点(true);

受A.Levy的详尽研究和Calvin的原型设计方法启发,我提供了以下解决方案:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

另请参见答案中的Andy Burke注释。

其他回答

我为这个问题提供了答案,因为我在这里没有看到任何解决DOM元素问题的本地递归实现。

问题是<element>具有父属性和子属性,这些属性链接到具有父值和子值的其他元素,这些元素指向原始<element>,从而导致无限递归或循环冗余。

如果你的目标是安全简单的

{
    '123':456
}

……那么这里的任何其他答案都可能奏效。

但如果你有。。。

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

…那么你需要这样的东西:

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

    if(variable === null) {
            return null;
    }

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}

复制最终可能指向自身的对象的问题可以通过简单的检查来解决。每次有复制操作时,添加此复选框。它可能很慢,但应该会起作用。

我使用toType()函数显式返回对象类型。我也有自己的copyObj()函数,它在逻辑上非常相似,可以回答所有三种Object()、Array()和Date()情况。

我在NodeJS中运行它。

尚未测试。

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}

在我的代码中,我经常定义一个函数(_)来处理副本,这样我就可以将值传递给函数。此代码创建一个深度副本,但保持继承。它还跟踪子副本,以便无需无限循环即可复制自引用对象。请随意使用。

这可能不是最优雅的,但它还没有让我失望。

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true

使用npm的深度复制。在浏览器和节点中作为npm模块工作。。。

https://www.npmjs.com/package/deepcopy

设a=深度复制(b)

可以使用rest运算符克隆阵列或对象

let myObj = {1: 100, 'a': 200};

let clone = {...myObj}; 

clone.a = 300;

console.log(clone.a) // Output :- 300
console.log(myObj.a) // Output :- 200