15

JavaScript でオブジェクトを複製しようとしています。プロトタイプ関数を持つ独自の「クラス」を作成しました。

私の問題:オブジェクトのクローンを作成すると、クローンはプロトタイプ関数にアクセス/呼び出すことができません。

クローンのプロトタイプ関数にアクセスしようとすると、エラーが発生します。

clone.render は関数ではありません

オブジェクトのクローンを作成し、そのプロトタイプ関数を維持する方法を教えてください

この単純な 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"
4

4 に答える 4

7

コードに関するいくつかのコメント:

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

することができます:

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

>    if (obj instanceof Array) {

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

ある配列のインデックスを別の配列にコピーするには、次を使用してより効率的かつ正確に行うことができますslice

      var copy = obj.slice();

ただし、数値ではない、追加された可能性のある他のプロパティを見逃すことになります。0をループして長さにすると、スパース配列に存在しないプロパティがクローンに追加されます(たとえば、エリジオンは未定義のメンバーになります)。

クローン部分は…</p>

オブジェクトプロパティをコピーする部分では、元の[[Prototype]]チェーン上のプロパティを含むすべてのプロパティを「クローン」オブジェクトに直接コピーします。オブジェクトを実際に「クローン」する唯一の方法は、オブジェクト[[Prototype]]を元のオブジェクトと同じオブジェクトに設定してから、元の(でフィルタリングされたhasOwnProperty)列挙可能なプロパティをクローンにコピーすることです。

prototype2番目の部分は些細なことですが、最初の部分は(一般的な意味で)そうではありません。オブジェクトのコンストラクタープロパティがそのオブジェクトを参照している[[Prototype]]ことを保証できず、コンストラクターのプロトタイプが変更されていないことを保証できないためです(つまり、その間、別のオブジェクト)。

最も近い方法は、Lasse Reichstein Nielsenclone(Douglas Crockfordによって人気がbegetあります)を使用して、元のオブジェクト[[Prototype]]をクローンのオブジェクトにし、コンストラクターを同じオブジェクトに設定することです。おそらく、列挙可能な独自のプロパティをコピーして、元の同じ名前のプロパティをマスクする必要があります。

したがって、実際には、制限されたコンテキスト内でのみオブジェクトのクローンを作成できます。通常、クローンを作成することはできません。そして一般的に、その実現は、オブジェクトを一般的に複製する必要がない設計につながります。

于 2012-04-14T06:46:43.883 に答える
4

関数は次のように単純化できます。

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

これが動作するjsfiddleです

于 2012-04-14T07:02:47.817 に答える
4

それ以外の

var copy = {};

使用する

var copy = new obj.constructor;
于 2012-04-14T05:25:31.777 に答える
2

複製するオブジェクトのコンストラクターを使用して、複製オブジェクトをインスタンス化します。

 var copy = {};

になります

 var copy = new obj.constructor();

それは迅速な対応であり、そのようなソリューションの欠点については考えていません (私は重いコンストラクターを考えています) が、一見するとうまくいくはずです (難解なメソッドについては言及しません (または頼りません__proto__))。

アップデート:

object.createこの問題を解決するには、に頼る必要があります。

 var copy = {};

になります

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

このように、元のコンストラクターはオブジェクトを作成するために呼び出されません (サーバーからデータを取得するために長い ajax 呼び出しを行うコンストラクターを考えてください)。

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

また、使用している JavaScript エンジンがこの機能をサポートしていない場合は、このコードを使用できます (ecmascript 5 仕様に追加されました)。

于 2012-04-14T05:34:17.473 に答える