我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
有很多答案,但没有一个提到ECMAScript 5中的Object.create,它确实没有给您一个确切的副本,而是将源设置为新对象的原型。
因此,这不是问题的确切答案,但它是一个单行解决方案,因此很优雅。它最适合2种情况:
在这种继承有用的地方(duh!)源对象不会被修改,因此这两个对象之间的关系不成问题。
例子:
var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property
为什么我认为这种解决方案更优越?它是本地的,因此没有循环,没有递归。然而,较旧的浏览器将需要polyfill。
其他回答
本文来自Brian Huisman的《如何在Javascript中复制数组和对象》:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (i == 'clone') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
} else newObj[i] = this[i]
} return newObj;
};
function clone(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:
var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references
obj2.text = 'moo2'; // Only updates obj2's text property
console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
对于当前不支持Object.create的浏览器/引擎,可以使用以下polyfill:
// Polyfill Object.create if it does not exist
if (!Object.create) {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
ES6解决方案,如果您想要(浅层)克隆类实例而不仅仅是属性对象。
在我的代码中,我经常定义一个函数(_)来处理副本,这样我就可以将值传递给函数。此代码创建一个深度副本,但保持继承。它还跟踪子副本,以便无需无限循环即可复制自引用对象。请随意使用。
这可能不是最优雅的,但它还没有让我失望。
_ = 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