Javascript 克隆对象失去了它的原型功能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10151216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 08:50:49  来源:igfitidea点击:

Javascript cloned object looses its prototype functions

javascript

提问by sazr

I am attempting to clone an object in Javascript. I have made my own 'class' that has prototype functions.

我试图在 Javascript 中克隆一个对象。我制作了自己的具有原型功能的“类”。

My Problem:When I clone an object, the clone cant access/call any prototype functions.

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

I get an error when I go to access a prototype function of the clone:

当我去访问克隆的原型函数时出现错误:

clone.render is not a function

clone.render 不是函数

Can you tell me how I can clone an object and keep its prototype functions

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

This simple JSFiddle demonstrates the error I get: http://jsfiddle.net/VHEFb/1/

这个简单的 JSFiddle 演示了我得到的错误:http: //jsfiddle.net/VHEFb/1/

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"

采纳答案by RobG

Some comments on the code:

对代码的一些评论:

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

can be:

可:

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

and

>    if (obj instanceof Array) {

will return false if obj is an array from another global context, such as an iFrame. Consider:

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

     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]);
>      }

Copying the indexes of one array to another can be done more efficiently and accurately using slice:

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

      var copy = obj.slice();

though you will miss any other properties that might have been added that aren't numeric. Looping over 0 to length will add properties to the clone that don't exist in a sparse array (e.g. elisions will become undefined members).

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

As for the cloning part…

至于克隆部分……

In the part copying object properties, that will copy all the properties, including those on the original's [[Prototype]]chain, directly to the "clone" object. The only way to really "clone" an object is to set its [[Prototype]]to the same object as the original, then copy the enumerable properties on the original (filtered with hasOwnProperty) to the clone.

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

The second part is trivial, the first part is not (in a general sense) since you can't guarantee that an object's constructor property references the object whose prototypeis its [[Prototype]], nor can you guarantee that the constructor's prototype hasn't changed (i.e. is a different object) in the meantime.

第二部分是微不足道的,第一部分是不是(在一般意义上),因为你不能保证对象的constructor属性引用它的对象prototype就是它[[Prototype]],也不能保证构造函数的原型并没有改变(即是一个不同的对象)同时。

The closest you can get is to use Lasse Reichstein Nielsen's clone(popularised by Douglas Crockford as beget) which makes the original object the [[Prototype]]of the clone, and then set the constructor to the same object. Though you probably still need to copy over the enumerable own properties so they mask the original's same-named properties.

你能得到的最接近的是使用Lasse Reichstein Nielsen 的clone(由 Douglas Crockford 推广beget),它使原始对象[[Prototype]]成为克隆的对象,然后将构造函数设置为相同的对象。尽管您可能仍然需要复制可枚举的自己的属性,以便它们掩盖原始的同名属性。

So you can really only clone an object within a restricted context, you can't do it generally. And generally that realisation leads to a design where you don't need to generically clone objects.

所以你真的只能在受限的上下文中克隆一个对象,你一般不能这样做。通常,这种实现会导致您不需要一般地克隆对象的设计。

回答by KooiInc

Your function can be simplified to:

您的功能可以简化为:

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.");
}

Here's a working jsfiddle

这是一个有效的 jsfiddle

回答by Delan Azabani

Instead of

代替

var copy = {};

use

利用

var copy = new obj.constructor;

回答by Eineki

I would instance the clone object using the constructor of the object to be cloned:

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

 var copy = {};

will be

将会

 var copy = new obj.constructor();

It is a quick response and I haven't pondered about drawbacks of such solution (I'm thinking of heavy constructor) but, at a first glance it should work (I wouldn't mention (or resort to) esoteric methods as __proto__).

这是一个快速响应,我没有考虑过这种解决方案的缺点(我正在考虑重型构造函数)但是,乍一看它应该可以工作(我不会提到(或诉诸)深奥的方法__proto__)。

Update:

更新:

you should resort to object.createto solve this problem.

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

 var copy = {};

will be

将会

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

In this way the original constructor is not called to create the object (think of a constructor that does an lengthy ajax call to retrieve data from server) as Object.create is equivalent to

通过这种方式,不会调用原始构造函数来创建对象(想想一个构造函数,它执行冗长的 ajax 调用以从服务器检索数据),因为 Object.create 等效于

Object.create = function (proto) {  
    function F() {}  
    F.prototype = proto;  
    return new F();  
};  

And you can use this code if the javascript engine you are using does not support this function (it was added to the ecmascript 5 specs)

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