JavaScript ES6 sınıf örneği nasıl klonlanır


96

ES6 kullanarak bir Javascript sınıfı örneğini nasıl klonlarım.

Jquery veya $ extension'a dayalı çözümlerle ilgilenmiyorum.

Sorunun oldukça karmaşık olduğunu öne süren oldukça eski nesne klonlama tartışmaları gördüm, ancak ES6 ile çok basit bir çözüm kendini gösteriyor - aşağıya koyacağım ve insanların tatmin edici olup olmadığını göreceğim.

düzenleme: sorumun bir kopya olduğu öneriliyor; Cevabı gördüm ama 7 yaşında ve ES6 öncesi js kullanarak çok karmaşık cevaplar içeriyor. ES6'ya izin veren sorumun çok daha basit bir çözüme sahip olduğunu öne sürüyorum.


2
Stack Overflow'daki eski bir soru için yeni bir cevabınız varsa, lütfen bu cevabı orijinal soruya ekleyin, sadece yeni bir cevap oluşturmayın.
Kafir Maymun

1
ES6 sınıfı örnekleri "normal" Nesnelerden farklı çalıştığı için Tom'un karşılaştığı / karşılaştığı sorunu görüyorum.
CherryNerd

2
Ayrıca, kabul edilen yanıttaki ilk kod parçası "olası yinelenen" bir ES6 sınıfı örneği üzerinde çalıştırmaya çalıştığımda çökmeler sağlıyor
CherryNerd

Bunun bir kopya olmadığını düşünüyorum, çünkü ES6 sınıf örneği bir nesne olmasına rağmen, her nesne ES6 sınıf örneği değildir ve bu nedenle diğer soru bu sorunun konusunu ele almamaktadır.
Tomáš Zato - Monica'yı eski haline getir

5
Kopya değildir. Diğer soru, Objectveri tutucu olarak kullanılan saflarla ilgiliydi . Bu, ES6'lar classve sınıf türü bilgilerini kaybetmeme sorunu ile ilgilidir. Farklı bir çözüme ihtiyacı var.
flori

Yanıtlar:


111

Karmaşık bir durum; Ben çok denedim! Sonunda, bu tek satırlık özel ES6 sınıfı örneklerim için çalıştı:

let clone = Object.assign(Object.create(Object.getPrototypeOf(orig)), orig)

Prototipi belirlemekten kaçınıyor çünkü kodu çok yavaşlattığını söylüyorlar .

Sembolleri destekler ancak alıcılar / ayarlayıcılar için mükemmel değildir ve numaralandırılamayan özelliklerle çalışmaz (bkz. Object.assign () docs ). Ayrıca, temel dahili sınıfları (Array, Date, RegExp, Map vb.) Klonlamak ne yazık ki çoğu zaman bazı bireysel işlemlere ihtiyaç duyuyor gibi görünüyor.

Sonuç: Bu bir karmaşa. Bir gün yerel ve temiz bir klon işlevi olacağını umalım.


1
Bu, statik yöntemleri kopyalamayacaktır çünkü bunlar aslında kendi özellikleri numaralandırılamazlar.
Mr. Lavalamp

5
@ Bay Lavalamp ve statik yöntemleri (aynı zamanda) nasıl kopyalayabilirsiniz?
flori

bu dizileri yok edecek! Tüm dizileri "0", "1", ... anahtarlarıyla nesnelere dönüştürür.
Vahid

1
@KeshaAntonov typeof ve Array yöntemleriyle bir çözüm bulmanız mümkün olabilir. Tüm özellikleri manuel olarak klonlamayı tercih ettim.
Vahid

1
Kendileri nesne olan özellikleri klonlamasını beklemeyin: jsbin.com/qeziwetexu/edit?js,console
jduhls

10
const clone = Object.assign( {}, instanceOfBlah );
Object.setPrototypeOf( clone, Blah.prototype );

Object.assign'ın özelliklerine dikkat edin : basit bir kopya oluşturur ve sınıf yöntemlerini kopyalamaz.

Kopya üzerinde derin bir kopya veya daha fazla kontrol istiyorsanız, lodash klonlama fonksiyonları vardır .


2
Yana Object.createneden olmasın sonra sadece belirtilen prototip ile yeni bir nesne oluşturur const clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah). Ayrıca sınıf yöntemleri de kopyalanacaktır.
barbatus

1
@barbatus Yanlış prototip kullanıyor Blah.prototype != instanceOfBlah. KullanmalısınızObject.getPrototypeOf(instanceOfBlah)
Bergi

1
@Bergi hayır, ES6 sınıfı örneğinin her zaman bir prototipi yoktur. Örnekle de çalıştığını codepen.io/techniq/pen/qdZeZm kontrol edin .
barbatus

@barbatus Pardon, ne var? Ben takip etmiyorum Tüm örneklerin bir prototipi vardır, onları örnek yapan şey budur. Flori'nin cevabındaki kodu deneyin.
Bergi

1
@Bergi Bence Babel konfigürasyonuna falan bağlı. Şu anda reaktif bir yerel uygulama uyguluyorum ve miras alınmış özellikleri olmayan örneklerde prototip boş var. Ayrıca burada da görebileceğiniz gibi developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… getPrototypeOf'un boş döndürmesi mümkündür.
barbatus

3

Prototipin uzantılarının yapılması tavsiye edilmez, Kodunuz / bileşenleriniz üzerinde testler yapacağınız zaman sorunlara neden olacaktır. Birim testi çerçeveleri otomatik olarak sizin prototip uzantılarınızı üstlenmez. Yani bu iyi bir uygulama değil. Burada prototip uzantılarının daha fazla açıklaması var Yerel nesneleri genişletmek neden kötü bir uygulama?

JavaScript'te nesneleri klonlamanın basit veya doğrudan bir yolu yoktur. "Sığ Kopya" kullanan ilk örnek:

1 -> Sığ klon:

class Employee {
    constructor(first, last, street) {
        this.firstName = first;
        this.lastName = last;
        this.address = { street: street };
    }

    logFullName() {
        console.log(this.firstName + ' ' + this.lastName);
    }
}

let original = new Employee('Cassio', 'Seffrin', 'Street A, 23');
let clone =  Object.assign({},original); //object.assing() method
let cloneWithPrototype Object.create(Object.getPrototypeOf(original)), original) //  the clone will inherit the prototype methods of the original.
let clone2 = { ...original }; // the same of object assign but shorter sintax using "spread operator"
clone.firstName = 'John';
clone.address.street = 'Street B, 99'; //will not be cloned

Sonuçlar:

original.logFullName ():

sonuç: Cassio Seffrin

clone.logFullName ():

sonuç: John Seffrin

original.address.street;

sonuç: 'Sokak B, 99' // orijinal alt nesnenin değiştirildiğine dikkat edin

Uyarı: Örneğin, kendi özellikleri olarak kapanışlara sahipse, bu yöntem onu ​​sarmaz. ( kapanışlar hakkında daha fazlasını okuyun ) Ve artı, "adres" alt nesnesi klonlanmayacaktır.

clone.logFullName ()

çalışmayacak.

cloneWithPrototype.logFullName ()

çalışacak, çünkü klon aynı zamanda Prototiplerini de kopyalayacaktır.

Object.assign ile dizileri klonlamak için:

let cloneArr = array.map((a) => Object.assign({}, a));

ECMAScript yayılma sintax kullanarak dizi klonlayın:

let cloneArrSpread = array.map((a) => ({ ...a }));

2 -> Derin Klon:

Tamamen yeni bir nesne referansını arşivlemek için, orijinal nesneyi dize olarak ayrıştırmak için JSON.stringify () kullanabiliriz ve sonra onu tekrar JSON.parse () olarak ayrıştırabiliriz.

let deepClone = JSON.parse(JSON.stringify(original));

Derin klonlama ile adres referansları korunacaktır. Ancak deepClone Prototipleri kaybedilecek, bu nedenle deepClone.logFullName () çalışmayacaktır.

3 -> 3. taraf kitaplıkları:

Diğer seçenekler, yükleme veya alt çizgi gibi 3. taraf kitaplıklarını kullanmak olacaktır. Yeni bir nesne oluştururlar ve her değeri orijinalden yeni nesneye referanslarını bellekte tutarak kopyalarlar.

Alt Çizgi: let cloneUnderscore = _ (orijinal) .clone ();

Loadash klonu: var cloneLodash = _.cloneDeep (orijinal);

Lodash veya alt çizginin dezavantajı, projenize bazı ekstra kütüphaneler dahil etme ihtiyacıydı. Ancak iyi seçeneklerdir ve aynı zamanda yüksek performanslı sonuçlar üretirler.


1
Atama yapılırken {}, klon orijinalin prototip yöntemlerinden herhangi birini devralmaz. clone.logFullName()hiç çalışmayacak. Daha Object.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)önce sahip olduğun iyiydi, neden bunu değiştirdin?
Bergi

1
@Bergi teşekkürler, şu anda cevabımı düzenliyordum, prototipleri kopyalamak için noktanızı ekledim!
Cassio Seffrin

1
Yardımın için minnettarım @ Bergi, Lütfen şimdi fikrinizi bildirin. Baskıyı bitirdim. Sanırım şimdi yanıt neredeyse tüm soruyu kapsıyor. Thks!
Cassio Seffrin

1
Evet ve Object.assign({},original)aynı şekilde çalışmıyor.
Bergi

1
bazen tek ihtiyacımız olan daha basit bir yaklaşımdır. Prototiplere ihtiyacınız yoksa ve karmaşık nesneler sadece "clone = {... original}" sorunu çözebilir
Cassio Seffrin

0

Başka bir astar:

Çoğu zaman ... (Date, RegExp, Map, String, Number, Array için çalışır), btw, cloning string, number biraz eğlencelidir.

let clone = new obj.constructor(...[obj].flat())

kopya yapıcısı olmayan sınıflar için:

let clone = Object.assign(new obj.constructor(...[obj].flat()), obj)

0
class A {
  constructor() {
    this.x = 1;
  }

  y() {
    return 1;
  }
}

const a = new A();

const output = Object.getOwnPropertyNames(Object.getPrototypeOf(a)).concat(Object.getOwnPropertyNames(a)).reduce((accumulator, currentValue, currentIndex, array) => {
  accumulator[currentValue] = a[currentValue];
  return accumulator;
}, {});

görüntü açıklamasını buraya girin


-4

Örneğin, Obj adlı bir nesneyi klonlamak istiyorsanız, yayma işlecini kullanabilirsiniz:

let clone = { ...obj};

Ve klonlanan nesneye herhangi bir şey eklemek veya değiştirmek isterseniz:

let clone = { ...obj, change: "something" };
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.