3 maneiras de clonar objetos em JavaScript

Samantha Ming Seg. 22 de jul · 6 min ler CodeTidbit de SamanthaMing.com

Como os objetos em #JavaScript são valores de referência, você não pode simplesmente copiar usando o = . Mas não se preocupe, aqui estão 3 maneiras para você clonar um objeto ?

 comida constante = {carne: '?', bacon: '?'} 
// "Espalhar"
{ ...Comida }
// "Object.assign"
Object.assign ({}, comida)
// "JSON"
JSON.parse (JSON.stringify (food))
// RESULTADO:
// {beef: '?', bacon: '?'}

Objetos são tipos de referência

Sua primeira pergunta pode ser: não posso usar = . Vamos ver o que acontece se fizermos isso:

 const obj = {um: 1, dois: 2}; const obj2 = obj; console.log ( 
obj, // {um: 1, dois: 2};
obj2 // {um: 1, dois: 2};
)

Até agora, ambos os objetos parecem produzir a mesma coisa. Então não há problema, certo. Mas vamos ver o que acontece se editarmos nosso segundo objeto:

 const obj2.three = 3; console.log (obj2); 
// {um: 1, dois: 2, três: 3}; <- ?
console.log (obj);
// {um: 1, dois: 2, três: 3}; <- ?

WTH ?! Eu mudei o obj2 mas porque o obj também foi afetado. Isso é porque os objetos são tipos de referência. Então, quando você usa = , copiou o ponteiro para o espaço de memória que ocupa. Os tipos de referência não contêm valores, eles são um ponteiro para o valor na memória.

Se você quiser saber mais sobre isso, confira o curso Zhu Watch and Code de Gordon. É grátis se inscrever e assistir ao vídeo “Comparação com objetos”. Ele dá uma explicação super incrível sobre isso.

Usando o Spread

Usar o spread clonará seu objeto. Note que esta será uma cópia superficial. A partir deste post, o operador de propagação para clonagem de objetos está no estágio 4. Por isso, ainda não está oficialmente nas especificações. Então, se você fosse usar isso, você precisaria compilá-lo com Babel (ou algo similar).

 alimento const = {carne bovina: '?', bacon: '?'}; const cloneFood = {... comida}; console.log (cloneFood); 
// {beef: '?', bacon: '?'}

Usando Object.assign

Como alternativa, o Object.assign está no Object.assign oficial e também cria uma cópia superficial do objeto.

 alimento const = {carne bovina: '?', bacon: '?'}; const cloneFood = Object.assign ({}, comida); console.log (cloneFood); 
// {beef: '?', bacon: '?'}

Usando JSON

Este último caminho lhe dará uma cópia profunda. Agora eu vou mencionar, esta é uma maneira rápida e suja de clonagem profunda de um objeto. Para uma solução mais robusta, eu recomendaria usar algo como lodash

 alimento const = {carne bovina: '?', bacon: '?'}; const cloneFood = JSON.parse (JSON.stringify (comida)) console.log (cloneFood); 
// {beef: '?', bacon: '?'}

Lodash DeepClone vs JSON

Aqui está um comentário da comunidade. Sim, foi para o meu post anterior, How to Deep Clone an Array . Mas a ideia ainda se aplica a objetos.

Alfredo Salzillo : Gostaria que você notasse que existem algumas diferenças entre o deepClone e o JSON.stringify / parse.

  • JSON.stringify / parse somente trabalha com literal de número e string e objeto sem função ou propriedades de símbolo.
  • O trabalho do deepClone com todos os tipos, funções e símbolos é copiado por referência.

Aqui está um exemplo:

 const lodashClonedeep = require ("lodash.clonedeep"); const arrOfFunction = [() => 2, { 
teste: () => 3,
}, Symbol ('4')];
// cópia deepClone por função refence e símbolo
console.log (lodashClonedeep (arrOfFunction));
// JSON substitui a função com null e a função no objeto com undefined
console.log (JSON.parse (JSON.stringify (arrOfFunction)));
// função e símbolo são copiados por referência em deepClone
console.log (lodashClonedeep (arrOfFunction) [0] === lodashClonedeep (arrOfFunction) [0]);
console.log (lodashClonedeep (arrOfFunction) [2] === lodashClonedeep (arrOfFunction) [2]);

@ OlegVaraksin : O método JSON tem problemas com dependências circulares. Além disso, a ordem das propriedades no objeto clonado pode ser diferente.

Clone raso vs clone profundo

Quando usei spread … para copiar um objeto, estou apenas criando uma cópia superficial. Se o array estiver aninhado ou multidimensional, não funcionará. Vamos dar uma olhada:

 const nestedObject = { 
país: '??',
{
cidade: 'vancouver'
}
};
const shallowClone = {... nestedObject}; // Changed our cloned object
clonedNestedObject.country = '??'
clonedNestedObject.country.city = 'taipei';

Então mudamos nosso objeto clonado mudando a cidade. Vamos ver a saída.

 console.log (shallowClone); 
// {country: '??', {city: 'taipei'}} <- ?
console.log (nestedObject);
// {country: '??', {city: 'taipei'}} <- ?

Uma cópia superficial significa que o primeiro nível é copiado, os níveis mais profundos são referenciados.

Cópia profunda

Vamos dar o mesmo exemplo, mas aplicar uma cópia profunda usando "JSON"

 const deepClone = JSON.parse (JSON.stringify (nestedObject)); console.log (deepClone); 
// {country: '??', {city: 'taipei'}} <- ?
console.log (nestedObject);
// {country: '??', {city: 'vancouver'}} <- ?

Como você pode ver, a cópia detalhada é uma cópia verdadeira para objetos aninhados. Muitas vezes a cópia superficial do tempo é boa o suficiente, você realmente não precisa de uma cópia profunda. É como uma pistola de pregos contra um martelo. Na maioria das vezes o martelo está perfeitamente bem. Usando uma pistola de pregos para algumas pequenas artes e ofícios é muitas vezes um exagero, um martelo é bom. É tudo sobre como usar a ferramenta certa para o trabalho certo ?

atuação

Infelizmente, não posso escrever um teste para divulgação porque ainda não está oficialmente na especificação. No entanto, incluí-lo no teste para que você possa executá-lo no futuro ?. Mas o resultado mostra que Object.assign é muito mais rápido que o JSON .

Teste de performance

Texto original em inglês.