JavaScript克隆“类”实例

20
我有一个这样的类:
function Element(){
    this.changes = {};
}
现在我有一个"Class"的实例,像这样:el = new Element()。这些实例被存储在一个数组中,像这样:elements.push(el)
现在,这个元素数组被存储在一个对象中,然后将该对象推入数组states中。
现在有时候我需要一个元素的副本,所以我需要做类似这样的事情:var cloned = $.extend(true, {}, states[0])。这里我假设我们克隆了第一个状态。
问题是,我得到的state[1].elements[0]仍然指向原始实例。因此,我对克隆对象所做的任何更改都会同时更改原始实例。
陷入这样微不足道的问题真是让人沮丧...
这是我创建的一个fiddle来测试它:http://jsfiddle.net/E6wLW/

1
我似乎无法复制这个,看看这个jsFiddle - jabclab
7
看这个:https://dev59.com/A3RB5IYBdhLWcg3wET5J - Samich
我刚刚添加了一个链接到一个 jsfiddle 代码片段。 - Amit
另外,我对问题进行了更多的编辑。 - Amit
你想要做的是所谓的“深拷贝”操作。一个非常简单的方法,根据你的对象内容和性能需求而定,就是将一个对象进行JSON编码,然后再解码它。 - Pointy
1
正确的解决方案是重新设计算法,以消除对深度复制的需求。深度复制 a) 很难做到正确,b) 计算成本高昂,c) 会创建依赖于所有内容都进行深度复制的逻辑。深度复制是一个难以解决的问题,应该通过使用浅拷贝来避免。 - Raynos
2个回答

5
$.extend 只会克隆纯粹的对象。如果该对象有构造函数,则不会被克隆,而是被复制。

$.extend源代码可以看出:

if ( jQuery.isPlainObject(copy) /* ... */) {
  // do the recursive $.extend call and clone the object                
} else if ( copy !== undefined ) {
  target[ name ] = copy;
  // ^^^^^ just copy
}

因此,$.extend()会调用isPlainObject(el),该函数将返回false,因为el具有构造函数,并且不是克隆el,而是复制了它。因此,states[1].elements[0]states[0].elements[0]是相同的对象,因为它没有被克隆。

如果我们修改你的例子:

function Element(){
  this.changes = {};
}
var el = new Element();    // $.isPlainObject(el); <- false
// ...

into:

var el = { changes: {} };  // $.isPlainObject(el); <- true
// ...

它将正确地克隆el。在这里查看:HERE

所以基本上jQuery的extend方法很愚蠢。 - Raynos
@Raynos $.extend() 无法完全支持带有构造函数的对象,因为它不知道要使用哪些参数。 - kubetz
使用哪些参数并不重要。他们所需要做的就是逐一复制对象属性。这很容易做到。 - Raynos
@Raynos,这还不是你需要做的全部,因为你拥有对象的所有属性,但原型不正确,离“克隆”还差得远。或者我漏掉了什么吗? :) - kubetz
1
忽略大量的边缘情况和黑魔法,克隆函数的核心非常简单。主要问题在于宿主对象和具有内部魔法的本地对象。哦,还有避免深度复制中的循环引用。 - Raynos
显示剩余2条评论

1

这样做不行。请阅读您发布的链接:任何嵌套的对象或数组都将被引用复制,而不是复制。 - kubetz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接