且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在javascript中深度克隆

更新时间:2023-01-17 16:00:12

这取决于你想要克隆的内容。这是一个真正的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.

你决定。