我有一个物体x
。我想将其复制为对象y
,这样更改y
就不会修改x
。我意识到,复制从内置JavaScript对象派生的对象将导致额外的不需要的属性。这不是问题,因为我正在复制自己的文字构造对象之一。
如何正确克隆JavaScript对象?
我有一个物体x
。我想将其复制为对象y
,这样更改y
就不会修改x
。我意识到,复制从内置JavaScript对象派生的对象将导致额外的不需要的属性。这不是问题,因为我正在复制自己的文字构造对象之一。
如何正确克隆JavaScript对象?
由于mindeavor指出要克隆的对象是“文字构造的”对象,因此一种解决方案可能是简单地多次生成该对象,而不是克隆该对象的实例:
function createMyObject()
{
var myObject =
{
...
};
return myObject;
}
var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
我只是想添加到本文中的所有Object.create
解决方案中,以至于nodejs无法以所需的方式工作。
在Firefox中,结果
var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´
是
{test:"test"}
。
在nodejs中
{}
对于深层复制和克隆,先JSON.stringify然后再JSON.parse该对象:
obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
旧问题的新答案!如果您高兴地将ECMAScript 2016(ES6)与Spread Syntax一起使用,这很容易。
keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
这为对象的浅表副本提供了一种干净的方法。进行深层复制(意味着要递归地嵌套每个对象中的每个值的新副本)需要上述较重的解决方案。
JavaScript不断发展。
我认为有一个简单而可行的答案。在深度复制中,有两个问题:
因此,我认为一个简单的解决方案是首先进行序列化和反序列化,然后对其进行赋值以复制函数。
let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);
尽管这个问题有很多答案,但我希望这个问题也能有所帮助。
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
ES6解决方案,如果您想(浅)克隆一个类实例,而不仅仅是一个属性对象。
使用Lodash:
var y = _.clone(x, true);
在ECMAScript 2018中
let objClone = { ...obj };
请注意,嵌套对象仍将被复制为参考。
答:Levy的答案几乎是完整的,这是我的一点贡献:有一种方法可以处理递归引用,请参见此行
if(this[attr]==this) copy[attr] = copy;
如果对象是XML DOM元素,我们必须使用cloneNode代替
if(this.cloneNode) return this.cloneNode(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的注释。
一种特别优雅的解决方案是使用JSON编码来制作没有成员方法的对象的深层副本。方法是对目标对象进行JSON编码,然后通过对其进行解码,获得所需的副本。您可以根据需要解码任意数量的副本。
当然,函数不属于JSON,因此仅适用于没有成员方法的对象。
这种方法非常适合我的用例,因为我将JSON Blob存储在键值存储中,并且当它们在JavaScript API中作为对象公开时,每个对象实际上都包含该对象原始状态的副本,因此我们可以在调用者更改了暴露对象后计算增量。
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
对于使用AngularJS的用户,此库中还有直接方法来克隆或扩展对象。
var destination = angular.copy(source);
要么
angular.copy(source, destination);
更多angular.copy 文档 ...
好的,假设您在下面有这个对象并且想要克隆它:
let obj = {a:1, b:2, c:3}; //ES6
要么
var obj = {a:1, b:2, c:3}; //ES5
答案主要取决于您使用哪种ECMAscript,在ES6+
中您可以简单地使用它Object.assign
来进行克隆:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
或像这样使用传播运算符:
let cloned = {...obj}; //new {a:1, b:2, c:3};
但是,如果您使用ES5
,则可以使用几种方法,但是JSON.stringify
,请确保不要使用要复制的大量数据,但是在许多情况下,这可能是一种方便的方式,例如:
let cloned = JSON.parse(JSON.stringify(obj));
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
如果您可以使用浅表副本,那么underscore.js库提供了一个clone方法。
y = _.clone(x);
或者你可以像扩展它
copiedObject = _.extend({},originalObject);
使用jQuery,您可以使用extend进行浅表复制:
var copiedObject = jQuery.extend({}, originalObject)
对的后续更改copiedObject
不会影响originalObject
,反之亦然。
或进行深复制:
var copiedObject = jQuery.extend(true, {}, originalObject)
在ECMAScript 6中,存在Object.assign方法,该方法将所有可枚举的自身属性的值从一个对象复制到另一个对象。例如:
var x = {myProp: "value"};
var y = Object.assign({}, x);
但是请注意,嵌套对象仍会复制为引用。
有很多答案,但是没有一个提到ECMAScript 5 中的Object.create,它当然不能提供确切的副本,但是会将源设置为新对象的原型。
因此,这不是对该问题的确切答案,而是单线解决方案,因此很优雅。它最适合2种情况:
例:
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。