如何在javascript中深度克隆 [英] How to Deep clone in javascript

查看:113
本文介绍了如何在javascript中深度克隆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你如何深度克隆Javascript对象?

How do you deep clone a Javascript object?

我知道有很多基于框架的函数,比如 JSON.parse(JSON.stringify (o)) $ .extend(true,{},o)但我不想使用这样的框架。

I know there are various functions based on frameworks like JSON.parse(JSON.stringify(o)) and $.extend(true, {}, o) but I don't want to use a framework like that.

创建深度克隆的最优雅或最有效的方法是什么。

What is the most elegant or efficient way to create a deep clone.

我们关心像克隆数组这样的边缘情况。不打破原型链,处理自引用。

We do care about edge cases like cloning array's. Not breaking prototype chains, dealing with self reference.

我们不关心支持复制DOM对象,因为 .cloneNode 因此而存在。

We don't care about supporting copying of DOM objects and like because .cloneNode exists for that reason.

因为我主要想在 node.js 中使用深度克隆V8发动机的ES5功能是可以接受的。

As I mainly want to use deep clones in node.js using ES5 features of the V8 engine is acceptable.

在有人建议让我提及之前,通过原型创建副本之间存在明显差异继承自对象和克隆它。前者使原型链变得混乱。

Before anyone suggests let me mention there is a distinct difference between creating a copy by prototypically inheriting from the object and cloning it. The former makes a mess of the prototype chain.

[进一步编辑]

阅读完答案后我来了令人讨厌的发现,克隆整个物体是一个非常危险和困难的游戏。以下面的基于闭包的对象为例

After reading your answer I came to the annoying discovery that cloning entire objects is a very dangerous and difficult game. Take for example the following closure based object

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

有没有办法编写克隆的克隆函数对象,在克隆时具有相同的状态,但不能在JS中编写JS解析器时改变 o 的状态。

Is there any way to write a clone function that clones the object, has the same state at time of cloning but cannot alter the state of o without writing a JS parser in JS.

不再需要这样的功能。这仅仅是学术兴趣。

There should be no real world need for such a function anymore. This is mere academic interest.

推荐答案

这取决于你想要克隆的内容。这是一个真正的JSON对象还是JavaScript中的任何对象?如果你想做任何克隆,它可能会让你遇到麻烦。哪个麻烦?我将在下面解释它,但首先是一个克隆对象文字,任何基元,数组和DOM节点的代码示例。

It really depends what you would like to clone. Is this a truly JSON object or just any object in JavaScript? If you would like to do any clone, it might get you into some trouble. Which trouble? I will explain it below, but first, a code example which clones object literals, any primitives, arrays and DOM nodes.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

现在,我们来谈谈开始克隆REAL对象时可能遇到的问题。我现在正在谈论关于你通过做类似的事情创建的对象

And now, let's talk about problems you might get when start cloning REAL objects. I'm talking now, about objects which you create by doing something like

var User = function(){}
var newuser = new User();

当然你可以克隆它们,这不是问题,每个对象都暴露构造函数属性,你可以用它来克隆对象,但它并不总是有效。你也可以在这个对象的中做简单的,但它会走向同一个方向 - 麻烦。我还在代码中包含了克隆功能,但它被排除了if(false)语句。

Of course you can clone them, it's not a problem, every object expose constructor property, and you can use it to clone objects, but it will not always work. You also can do simple for in on this objects, but it goes to the same direction - trouble. I have also included clone functionality inside the code, but it's excluded by if( false ) statement.

所以,为什么克隆可能是一种痛苦?好吧,首先,每个对象/实例可能都有一些状态。你永远无法确定你的对象没有私有变量,如果是这种情况,通过克隆对象,你就可以打破状态。

So, why cloning can be a pain? Well, first of all, every object/instance might have some state. You never can be sure that your objects doesn't have for example an private variables, and if this is the case, by cloning object, you just break the state.

想象一下,没有州,没关系。然后我们还有另一个问题。通过构造函数方法克隆将给我们带来另一个障碍。这是一个参数依赖。你永远无法确定,创建这个对象的人没有,某种

Imagine there is no state, that's fine. Then we still have another problem. Cloning via "constructor" method will give us another obstacle. It's an arguments dependency. You never can be sure, that someone who created this object, did not did, some kind of

new User({
   bike : someBikeInstance
});

如果是这种情况,你运气不好,someBikeInstance可能是在某些情况下创建的克隆方法的上下文是未知的。

If this is the case, you are out of luck, someBikeInstance was probably created in some context and that context is unkown for clone method.

那该怎么办?您仍然可以在解决方案中执行,并将此类对象视为普通对象文字,但也许根本不要克隆此类对象,只需传递此参考宾语?

So what to do? You still can do for in solution, and treat such objects like normal object literals, but maybe it's an idea not to clone such objects at all, and just pass the reference of this object?

另一个解决方案是 - 您可以设置一个约定,即必须克隆的所有对象应该自己实现此部分并提供适当的API方法(如cloneObject)。什么 cloneNode 正在为DOM做什么。

Another solution is - you could set a convention that all objects which must be cloned should implement this part by themselves and provide appropriate API method ( like cloneObject ). Something what cloneNode is doing for DOM.

你决定。

这篇关于如何在javascript中深度克隆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆