Jak sklonować instancję klasy JavaScript przy użyciu ES6.
Nie interesują mnie rozwiązania oparte na jquery czy $ extend.
Widziałem dość stare dyskusje na temat klonowania obiektów, które sugerują, że problem jest dość skomplikowany, ale w ES6 pojawia się bardzo proste rozwiązanie - zamieszczę je poniżej i zobaczę, czy ludzie uważają, że jest zadowalające.
edycja: sugeruje się, że moje pytanie jest duplikatem; Widziałem tę odpowiedź, ale ma 7 lat i obejmuje bardzo skomplikowane odpowiedzi przy użyciu js z wersji wcześniejszej niż ES6. Sugeruję, że moje pytanie, które pozwala na ES6, ma znacznie prostsze rozwiązanie.
Object
s wykorzystywanych jako posiadacze danych. Ten dotyczy ES6class
i problemu, aby nie stracić informacji o typie klasy. Potrzebuje innego rozwiązania.Odpowiedzi:
To skomplikowane; Bardzo się starałem! Ostatecznie ten jeden wiersz zadziałał dla moich niestandardowych instancji klasy ES6:
let clone = Object.assign(Object.create(Object.getPrototypeOf(orig)), orig)
Unika ustawiania prototypu, ponieważ mówi się, że bardzo spowalnia kod.
Obsługuje symbole, ale nie jest idealny dla metod pobierających / ustawiających i nie działa z niewliczalnymi właściwościami (patrz dokumentacja Object. assign () ). Ponadto klonowanie podstawowych klas wewnętrznych (takich jak Array, Date, RegExp, Map itp.) Niestety często wydaje się wymagać indywidualnej obsługi.
Wniosek: to bałagan. Miejmy nadzieję, że pewnego dnia pojawi się natywna i czysta funkcjonalność klonowania.
źródło
const clone = Object.assign( {}, instanceOfBlah ); Object.setPrototypeOf( clone, Blah.prototype );
Zwróć uwagę na charakterystykę Object. assign : wykonuje on płytką kopię i nie kopiuje metod klas.
Jeśli chcesz mieć głęboką kopię lub większą kontrolę nad kopią, dostępne są funkcje klonowania lodash .
źródło
Object.create
tworzy nowy obiekt z określonym prototypem, to czemu nie po prostuconst clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah)
. Kopiowane będą również metody klasowe.Blah.prototype != instanceOfBlah
. Powinieneś użyćObject.getPrototypeOf(instanceOfBlah)
Nie jest zalecane tworzenie rozszerzeń Prototypu, spowoduje to problemy podczas wykonywania testów na kodzie / komponentach. Struktury testów jednostkowych nie przyjmą automatycznie twoich prototypowych rozszerzeń. Więc to nie jest dobra praktyka. Więcej wyjaśnień dotyczących prototypowych rozszerzeń jest tutaj. Dlaczego rozszerzanie obiektów natywnych jest złą praktyką?
Nie ma prostego ani prostego sposobu na klonowanie obiektów w JavaScript. Oto pierwszy przypadek użycia „Shallow Copy”:
1 -> Płytki 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
Wyniki:
Uwaga: Jeśli instancja ma domknięcia jako własne właściwości, ta metoda jej nie zawinie. ( przeczytaj więcej o domknięciach ) Ponadto podobiekt „address” nie zostanie sklonowany.
nie będzie działać.
zadziała, ponieważ klon skopiuje również swoje prototypy.
Aby sklonować tablice za pomocą Object. assign:
let cloneArr = array.map((a) => Object.assign({}, a));
Klonuj macierz za pomocą sintax rozszerzania ECMAScript:
let cloneArrSpread = array.map((a) => ({ ...a }));
2 -> Głęboki klon:
Aby zarchiwizować zupełnie nowe odniesienie do obiektu, możemy użyć JSON.stringify () do przeanalizowania oryginalnego obiektu jako ciągu znaków, a po przeanalizowaniu go z powrotem do JSON.parse ().
let deepClone = JSON.parse(JSON.stringify(original));
Dzięki głębokiemu klonowaniu odniesienia do adresu zostaną zachowane. Jednak prototypy deepClone zostaną utracone, dlatego metoda deepClone.logFullName () nie będzie działać.
3 -> Biblioteki stron trzecich:
Inną opcją będzie użycie bibliotek innych firm, takich jak loadash lub podkreślenie. Tworzą nowy obiekt i kopiują każdą wartość z oryginału do nowego obiektu, zachowując odniesienia w pamięci.
Podkreślenie: niech cloneUnderscore = _ (oryginał) .clone ();
Loadash clone: var cloneLodash = _.cloneDeep (oryginał);
Wadą lodash lub podkreślenia była konieczność włączenia do projektu kilku dodatkowych bibliotek. Są to jednak dobre opcje, a także zapewniają wysoką wydajność.
źródło
{}
, klon nie odziedziczy żadnej z prototypowych metod oryginału.clone.logFullName()
w ogóle nie zadziała. ToObject.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)
, co miałeś wcześniej, było w porządku, dlaczego to zmieniłeś?Object.assign({},original)
nie działa.Kolejna wkładka:
W większości przypadków ... (działa dla Date, RegExp, Map, String, Number, Array), btw, cloning string, number jest trochę zabawne.
let clone = new obj.constructor(...[obj].flat())
dla tych klas bez konstruktora kopiującego:
let clone = Object.assign(new obj.constructor(...[obj].flat()), obj)
źródło
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; }, {});
źródło
Lubię prawie wszystkie odpowiedzi. Miałem ten problem i aby go rozwiązać, robiłem to ręcznie definiując
clone()
metodę, a wewnątrz niej budowałbym cały obiekt od podstaw. Dla mnie ma to sens, ponieważ wynikowy obiekt będzie naturalnie tego samego typu co sklonowany obiekt.Przykład z maszynopisem:
export default class ClassName { private name: string; private anotherVariable: string; constructor(name: string, anotherVariable: string) { this.name = name; this.anotherVariable = anotherVariable; } public clone(): ClassName { return new ClassName(this.name, this.anotherVariable); } }
Podoba mi się to rozwiązanie, ponieważ wygląda bardziej „obiektowo”
źródło
Możesz użyć operatora rozprzestrzeniania, na przykład, jeśli chcesz sklonować obiekt o nazwie Obj:
let clone = { ...obj};
A jeśli chcesz zmienić lub dodać cokolwiek do sklonowanego obiektu:
let clone = { ...obj, change: "something" };
źródło