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

如何正确克隆JavaScript对象?


当前回答

老问题的新答案!如果您有幸将ECMAScript 2016(ES6)与Spread Syntax一起使用,这很简单。

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

这为对象的浅层副本提供了一种干净的方法。创建一个深度副本,意味着为每个递归嵌套对象中的每个值创建一个新副本,这需要使用上面更重的解决方案。

JavaScript不断发展。

其他回答

Jan Turo的上述答案非常接近,由于兼容性问题,可能是在浏览器中使用的最佳选择,但这可能会导致一些奇怪的枚举问题。例如,执行:

for ( var i in someArray ) { ... }

在遍历数组元素后,将clone()方法赋给i。下面是一个避免枚举并适用于node.js的改编:

Object.defineProperty( Object.prototype, "clone", {
    value: 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;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

这避免了使clone()方法可枚举,因为defineProperty()默认为false。

在ES-6中,您可以简单地使用Object.assign(…)。前任:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

这里有一个很好的参考:https://googlechrome.github.io/samples/object-assign-es6/

您可以使用函数闭包来获得深度复制的所有好处,而无需深度复制。这是一个非常不同的范例,但效果很好。不要试图复制现有对象,只要在需要时使用函数实例化新对象即可。

首先,创建一个返回对象的函数

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

然后创建一个简单的浅层复制函数

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

创建一个新对象,并将模板的财产复制到其中

var newObject = {}; 
copy(newObject, template());

但上述复制步骤不是必需的。您需要做的就是:

var newObject = template();

现在您有了一个新对象,请测试它的财产:

console.log(Object.keys(newObject));

这将显示:

["values", "nest"]

是的,这些是newObject自己的财产,而不是对另一个对象上财产的引用。让我们检查一下:

console.log(newObject.nest.x.b);

这将显示:

"b"

newObject获得了模板对象的所有财产,但没有任何依赖链。

http://jsbin.com/ISUTIpoC/1/edit?js安慰

我添加此示例是为了鼓励一些讨论,因此请添加一些评论:)

一个特别不优雅的解决方案是使用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

我只是想在本文中添加到所有Object.create解决方案中,这并不能以nodejs所需的方式工作。

在Firefox中

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

is

{test:“test”}。

在nodejs中

{}