Como faço para clonar uma instância de classe Javascript usando ES6.
Não estou interessado em soluções baseadas em jquery ou $ extend.
Já vi discussões bastante antigas sobre clonagem de objetos que sugerem que o problema é bastante complicado, mas com o ES6 uma solução muito simples se apresenta - vou colocá-la abaixo e ver se as pessoas acham que é satisfatória.
editar: está sendo sugerido que minha pergunta é uma duplicata; Eu vi essa resposta, mas ela tem 7 anos e envolve respostas muito complicadas usando js pré-ES6. Estou sugerindo que minha pergunta, que permite o ES6, tem uma solução muito mais simples.
Object
s puros usados como suportes de dados. Este é sobre ES6class
es e o problema de não perder a informação do tipo de aula. Precisa de uma solução diferente.Respostas:
É complicado; Eu tentei muito! No final, esse one-liner funcionou para minhas instâncias de classe ES6 personalizadas:
let clone = Object.assign(Object.create(Object.getPrototypeOf(orig)), orig)
Isso evita definir o protótipo porque eles dizem que retarda muito o código.
Ele suporta símbolos, mas não é perfeito para getters / setters e não funciona com propriedades não enumeráveis (consulte a documentação Object.assign () ). Além disso, a clonagem de classes internas básicas (como Array, Date, RegExp, Map, etc.), infelizmente, muitas vezes parece precisar de algum tratamento individual.
Conclusão: é uma bagunça. Esperemos que um dia haja uma funcionalidade de clone nativa e limpa.
fonte
const clone = Object.assign( {}, instanceOfBlah ); Object.setPrototypeOf( clone, Blah.prototype );
Observe as características de Object.assign : ele faz uma cópia superficial e não copia métodos de classe.
Se você deseja uma cópia profunda ou mais controle sobre a cópia, existem as funções de clone do lodash .
fonte
Object.create
cria um novo objeto com o protótipo especificado, por que não apenasconst clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah)
. Além disso, os métodos de classe também serão copiados.Blah.prototype != instanceOfBlah
,. Você deve usarObject.getPrototypeOf(instanceOfBlah)
Não é recomendado fazer extensões do Protótipo, isso vai resultar em problemas quando você fizer testes em seu código / componentes. Os frameworks de teste de unidade não assumirão automaticamente suas extensões de protótipo. Portanto, não é uma boa prática. Há mais explicações sobre extensões de protótipo aqui. Por que estender objetos nativos é uma prática ruim?
Para clonar objetos em JavaScript, não existe uma maneira simples ou direta. Aqui está a primeira instância usando "Cópia superficial":
1 -> Clone raso:
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
Resultados:
Aviso: Se a instância tiver fechamentos como propriedades próprias, este método não irá envolvê-la. ( leia mais sobre fechamentos ) E mais, o subobjeto "endereço" não será clonado.
não funciona.
funcionará, porque o clone também copiará seus protótipos.
Para clonar matrizes com Object.assign:
let cloneArr = array.map((a) => Object.assign({}, a));
Array clone usando a sintaxe de propagação ECMAScript:
let cloneArrSpread = array.map((a) => ({ ...a }));
2 -> Clone Profundo:
Para arquivar uma referência de objeto completamente nova, podemos usar JSON.stringify () para analisar o objeto original como string e depois analisá-lo de volta para JSON.parse ().
let deepClone = JSON.parse(JSON.stringify(original));
Com o clone profundo, as referências ao endereço serão mantidas. No entanto, os Protótipos deepClone serão perdidos, portanto, o deepClone.logFullName () não funcionará.
3 -> Bibliotecas de terceiros:
Outra opção será usar bibliotecas de terceiros, como loadash ou sublinhado. Eles irão criar um novo objeto e copiar cada valor do original para o novo objeto mantendo suas referências na memória.
Sublinhado: deixe cloneUnderscore = _ (original) .clone ();
Clone Loadash: var cloneLodash = _.cloneDeep (original);
A desvantagem do lodash ou do sublinhado foi a necessidade de incluir algumas bibliotecas extras em seu projeto. Porém são boas opções e também produzem resultados de alto desempenho.
fonte
{}
, o clone não herdará nenhum dos métodos de protótipo do original.clone.logFullName()
não funcionará de todo. O queObject.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)
você tinha antes estava bom, por que você mudou isso?Object.assign({},original)
não funciona.Outro forro:
Na maioria das vezes ... (funciona para Date, RegExp, Map, String, Number, Array), btw, string de clonagem, número é um pouco engraçado.
let clone = new obj.constructor(...[obj].flat())
para aquelas classes sem construtor de cópia:
let clone = Object.assign(new obj.constructor(...[obj].flat()), obj)
fonte
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; }, {});
fonte
Gosto de quase todas as respostas. Tive esse problema e para resolvê-lo faria manualmente definindo um
clone()
método e dentro dele, construiria todo o objeto do zero. Para mim, isso faz sentido porque o objeto resultante será naturalmente do mesmo tipo que o objeto clonado.Exemplo com texto datilografado:
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); } }
Eu gosto desta solução porque parece mais 'Orientada a Objetos'
fonte
Você pode usar o operador spread, por exemplo, se quiser clonar um objeto chamado Obj:
let clone = { ...obj};
E se você quiser alterar ou adicionar algo ao objeto clonado:
let clone = { ...obj, change: "something" };
fonte