Javascript克隆对象失去了原型函数

Javascript cloned object looses its prototype functions

我正在尝试用JavaScript克隆一个对象。我已经创建了自己的具有原型函数的"类"。

我的问题是:当我克隆一个对象时,克隆无法访问/调用任何原型函数。

我在访问克隆的原型函数时出错:

clone.render is not a function

你能告诉我如何克隆一个对象并保留它的原型函数吗

这个简单的jsfidle演示了我得到的错误:http://jsfidle.net/vhefb/1/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function cloneObject(obj)
{
   // Handle the 3 simple types, and null or undefined
   if (null == obj ||"object" != typeof obj) return obj;

   // Handle Date
   if (obj instanceof Date) {
     var copy = new Date();
     copy.setTime(obj.getTime());
     return copy;
   }

   // Handle Array
   if (obj instanceof Array) {
     var copy = [];
     for (var i = 0, len = obj.length; i < len; ++i) {
         copy[i] = cloneObject(obj[i]);
     }
     return copy;
   }

   // Handle Object
   if (obj instanceof Object) {
     var copy = {};
     for (var attr in obj) {
         if (obj.hasOwnProperty(attr)) copy[attr] = cloneObject(obj[attr]);
     }
     return copy;
   }

   throw new Error("Unable to copy obj! Its type isn't supported.");
}

function MyObject(name)
{
    this.name = name;
    // I have arrays stored in this object also so a simple cloneNode(true) call wont copy those
    // thus the need for the function cloneObject();
}

MyObject.prototype.render = function()
{
    alert("Render executing:"+this.name);
}

var base  = new MyObject("base");
var clone = cloneObject(base);
clone.name ="clone";
base.render();
clone.render();  // Error here:"clone.render is not a function"


对代码的一些注释:

1
2
3
>    if (obj instanceof Date) {
>      var copy = new Date();
>      copy.setTime(obj.getTime());

可以是:

1
2
if (obj instanceof Date) {
  var copy = new Date(obj);

1
>    if (obj instanceof Array) {

如果obj是来自另一个全局上下文(如iframe)的数组,则返回false。考虑:

1
2
3
4
5
6
     if (o && !(o.constructor.toString().indexOf("Array") == -1))

>      var copy = [];
>      for (var i = 0, len = obj.length; i < len; ++i) {
>          copy[i] = cloneObject(obj[i]);
>      }

使用slice可以更有效、更准确地将一个数组的索引复制到另一个数组:

1
      var copy = obj.slice();

尽管您会错过任何其他可能添加的非数字属性。循环长度超过0将向稀疏数组中不存在的克隆添加属性(例如,省略将成为未定义的成员)。

至于克隆部分…

在复制对象属性的部分中,将所有属性(包括原始的[[Prototype]]链上的属性)直接复制到"clone"对象。真正"克隆"对象的唯一方法是将其[[Prototype]]设置为与原始对象相同的对象,然后将原始对象上的可枚举属性(用hasOwnProperty过滤)复制到克隆。

第二部分是琐碎的,第一部分不是(一般意义上),因为你不能保证一个对象的构造函数属性引用了prototype是其[[Prototype]]的对象,也不能保证构造函数的原型同时没有改变(即是另一个对象)。

最接近的方法是使用lasse reichstein-nielsen的clone(由Douglas Crockford推广为beget),使原始对象成为克隆的[[Prototype]],然后将构造函数设置为相同的对象。尽管您可能仍然需要复制可枚举的自己的属性,以便它们屏蔽原始属性的相同命名属性。

因此,您实际上只能在受限制的上下文中克隆一个对象,一般情况下不能这样做。通常,实现会导致一种设计,在这种设计中,您不需要一般地克隆对象。


您的功能可以简化为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function cloneObject(obj)
{
   obj = obj && obj instanceof Object ? obj : '';

   // Handle Date (return new Date object with old value)
   if (obj instanceof Date) {
     return new Date(obj);
   }

   // Handle Array (return a full slice of the array)
   if (obj instanceof Array) {
     return obj.slice();
   }

   // Handle Object
   if (obj instanceof Object) {
     var copy = new obj.constructor();
     for (var attr in obj) {
         if (obj.hasOwnProperty(attr)){
             if (obj[attr] instanceof Object){
                 copy[attr] = cloneObject(obj[attr]);
             } else {
                 copy[attr] = obj[attr];
             }
         }
     }
     return copy;
   }

   throw new Error("Unable to copy obj! Its type isn't supported.");
}

这是一把正在工作的J小提琴


而不是

1
var copy = {};

使用

1
var copy = new obj.constructor;


我将使用要克隆的对象的构造函数来实例克隆对象:

1
 var copy = {};

1
 var copy = new obj.constructor();

这是一个快速的响应,我没有考虑过这种解决方案的缺点(我考虑的是重型构造函数),但是,乍一看,它应该是有效的(我不会提到(或求助于)像__proto__这样的深奥方法)。

更新:

你应该求助于object.create来解决这个问题。

1
 var copy = {};

1
 var copy = Object.create(obj.constructor.prototype);

这样,就不会调用原始的构造函数来创建对象(考虑一个执行长时间Ajax调用以从服务器检索数据的构造函数),因为object.create等效于

1
2
3
4
5
Object.create = function (proto) {  
    function F() {}  
    F.prototype = proto;  
    return new F();  
};

如果您使用的javascript引擎不支持此功能(它被添加到ecmascript 5规范中),则可以使用此代码。