2012-04-14 17 views
9

Javascript'te bir nesneyi klonlamaya çalışıyorum. Prototip işlevlerine sahip kendi 'sınıfım' yaptım.Javascript klonlanmış nesneyi prototip işlevlerini kaybeder

Sorunum: Bir nesneyi klonladığımda, klon herhangi bir prototip işlevine erişemez/çağrı yapamaz.

clone.render bir nesne klonlayabilirsiniz nasıl

söyler misin bir fonksiyonu değildir: Ben klonu bir prototip işlevini erişmek giderken

bir hata alıyorum ve Bu basit JSFiddle hatasını izlediğini gösteren prototip fonksiyonları

tutmak alıyorum: 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" 
+0

var [bu çözüm] (http://stackoverflow.com/a/728694/575527) burada SO içinde . Ancak, bir nesneyi tam olarak klonlamanın açık bir yolu yoktur. [Bu bir jQuery kullanır] (http: // stackoverflow.com/q/122102/575527) – Joseph

cevap

7

kodu Bazı yorumlarınızı bu kodu kullanabilirsiniz:

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

olabilir:

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

ve

Obj, iFrame gibi başka bir genel içerikten bir dizi ise, false değerini döndürür. Düşünün:

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

daha verimli yapılabilir başka bir dizinin endeksleri kopyalanması ve doğru bir slice kullanarak:

 var copy = obj.slice(); 

değil mi eklenmiş olabilir başka özelliklerini özleyeceğim rağmen sayısal. Uzunluğa 0'dan fazla döngü yapmak, seyrek bir dizide bulunmayan klona özellikler ekleyecektir (örneğin, seçimler tanımsız üyeler haline gelecektir). doğrudan "klon" nesneye orijinalin [[Prototype]] zincire de dahil olmak üzere tüm özellikleri, kopyalar klonlama kısmına gelince

... parçası kopyalama nesne özelliklerinde

. Bir nesneyi gerçekten "klonlamak" için tek yol, [[Prototype]] değerini orijinalle aynı nesneye ayarlamak, ardından orijinaldeki (hasOwnProperty ile filtrelenerek) sayısız özellikleri kopyaya kopyalamaktır.

ikinci bölümü, bir nesnenin yapıcı özelliği olan prototype onun [[Prototype]] olduğunu nesneye başvurur garanti edemez beri ilk bölümü (genel anlamda) değil, önemsiz, ne sen şantiye prototip değişmedi garanti edemez Bu arada değişmedi (yani farklı bir nesne).

alabileceğiniz en yakın orijinal nesneyi klonu [[Prototype]] yapar (beget olarak Douglas Crockford tarafından popüler) Lasse Reichstein Nielsen's clone kullanın ve daha sonra aynı nesneye yapıcısı ayarlamaktır. Her ne kadar muhtemelen sayısız kendi özelliklerini kopyalamanız gerekiyorsa da, orijinalin aynı adlı özelliklerini saklıyorlar.

Bu nedenle, yalnızca bir nesneyi sınırlı bir bağlam içinde klonlayabilirsiniz, genellikle yapamazsınız. Ve genellikle bu gerçekleştirme, nesneleri genel olarak klonlamaya ihtiyaç duymadığınız bir tasarıma yol açar.

+0

Şimdi ben (daha iyi) noktanızı görüyorum. Yine de prototip hokkabazlığıyla ilgili referanslarla ilgilenirim. Bir tane var mı? – Eineki

+0

ES5, Object.create() 'ile kullanılabilecek [Object.getPrototypeOf()] (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf) ürününü tanıttı, ancak iyi değil henüz desteklenmedi. Muhtemelen göz atmayan [\ _ \ _ proto__] (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/Proto) için özellik testi ile çok sayıda tarayıcıyı kapsayabilir, ancak bunu kaçırırsınız. IE 8 öncesi ve önceki sürümlerde ve telefonlarda, oyun konsollarında vs. zillion gibi diğer küçük kullanıcı aracıları gibi ES5 tarayıcıları kontrol edin. [Kangax'ın arkadaş tablosu] (http://kangax.github.com/es5-compat-table/). – RobG

3

yerine

var copy = {}; 

kullanım

var copy = new obj.constructor; 
+0

Ancak başlangıçta aynı çözümü önerdim ama sanırım bunun ikinci bir düşünce olduğunu düşünüyorum. Yapımcı, davranışını her değiştiği zaman değiştirir. Farklı prototip zinciriyle bir tablonun sıralı sıraları. Biliyorum biraz uzatılmış bir örnektir ama bu bir seçenek – Eineki

2

Ben örnek nesnenin yapıcısı kullanılarak klon nesne klonlanmış olması olacaktır:

var copy = {}; 

olacak

var copy = new obj.constructor(); 

Bu hızlı bir yanıttır ve böyle bir çözümün dezavantajları hakkında düşünmedim (ağır yapıcıyı düşünüyorum) ama ilk bakışta çalışmalıyım (ezoterik olmayacağından bahsetmeyeceğim). __proto__) yöntemleri.

Güncelleme:

bu sorunu çözmek için object.create başvurmak gerekir.

var copy = {}; 

orijinal yapıcı Nesne olarak (sunucudan veri almak için bir uzun ajax arama yapan bir yapıcısı düşünün) nesne oluşturmak için çağrılmaz Bu şekilde

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

olacaktır.oluşturmak

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

eşdeğerdir Ve bu işlevi desteklemez kullandığınız JavaScript motoru (o ECMAScript'e 5 spesifikasyonları eklendi) eğer

+0

Ana konu, 'obj.constructor', oluşturulduğu nesneyi referans göstermeyebilir (örneğin, yukarıda kopyalanmış olan Crockford'un "beget'inin kullanılması). Nesneyi ilk etapta kurduğunuzdan emin olabilirsiniz ve bu durumda kurucuyu biliyorsunuz, neden doğrudan referans göstermiyorsunuz? – RobG

+0

Oh, ve yapıcının prototipi artık farklı bir nesne olabilir, böylece aynı kurucudan olsalar bile [[Prototip]]! == kopya [[Prototip]] ''. – RobG

+0

@RobG Anlaşılmadım, bir objeyi kopyalamak, istenen prototiple boş bir tane oluşturmak ve prototip zincirini korumak için orijinal prototipi kopyalamak istiyorum. Obj [[Prototype]] 'ı değiştirmek için bir metot vardır (bence bu sadece bir iç pervane olduğunu düşünüyorum) böylece obj.constructor.prototype'dan farklıdır? Böyle bir şansı (gerçekten) bilirim. – Eineki

4

İşleviniz basitleştiirlebilir:

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

i gördüğüm bir working jsfiddle

İlgili konular