Copiar objetos no javascript

Referencia de objetos é um assunto facil de entender mas que é muito facil cometer erros.

Marcos Junior | codermarcos
Frontend Quest
4 min readApr 17, 2018

--

Exemplo pratico

A idéia é entender oque acontece quando criamos um objeto e tentamos copia-lo, sem criar referência. Para isso vou criar uma variavel pessoa que sera um objeto e vai ser modificado mas antes deve ser clonado seu estado original.

Criar o objeto

Primeiro criamos um objeto na memoria

O código cria um objeto na memoria

Uma representação grafica do que acontece no javascript seria algo assim

Representação grafica de quando se cria um objeto que contem outros objetos

Copiar com referencia

Agora iremos “copiar” o objeto da forma mais simples atribuindo o objeto para a variavel clone desta forma

O codigo acima cria uma referencia do objeto pessoa

Internamente o javascript não criou outro objeto, ele criou uma refencia na memoria que aponta para pessoa

Representação grafica de quando se cria uma referência de um objeto

Por este motivo o clone sempre estara exatamente igual a pessoa

O codigo acima cria uma propriedade idade com valor 19 no objeto pessoa

Alterando o objeto pessoa automaticamente o clone reflete as alterações

Representação grafica de quando se cria uma propriedade em um objeto com referência

Isso ocorre quando se atribui uma variavel do tipo object diretamente. Ele vai apontar a posição na memoria daquela variavel object. Legal não é mesmo :D

Referências circulares

Por este motivo se você atribui um objeto ao outro e vice-versa tera uma referencia ciclica.

O codigo acima gera uma referência circular entre a e b

Isso pode gerar alguns problemas como por exemplo quando você tentar converter este object para string

O codigo retorna um exception ao tentar obter uma string de uma referência circular

Para resolver é bem simples basta criar um replacer para auxiliar o stringify

O codigo mostra como resolver o problema de tentar obter uma string de uma referência circular

Mas esse não é o unico problema se você ja conhece o GB (Garbage collection), sabe que ele é nosso algoritimo de limpar a memoria. No javascript ele sempre limpa o espaço da variavel assim que ela deixa de ser referenciada.

O codigo mostra um exemplo de coleta do GB para referência circulares

No exemplo acima a e b sempre seram referênciadas então o algoritimo considera que ambos os objetos estão sendo referênciados pelo menos uma vez então nenhum deles podem ser coletados da memoria.

O que há de errado com referências circulares?

  • Referências circulares criam alto acoplamento, ambas as classes devem ser recompiladas sempre que uma delas for alterada.
  • Referências circulares podem causar falhas em algoritmos recursivos ingênuos.
  • Objetos com um número muito grande de referências circulares mesmo que não sejam, eles tendem a levar ao código macarronico.
  • Referências circulares em geral são simplesmente confusas e aumentam drasticamente a carga cognitiva ao tentar entender como um programa funciona.

Copiar sem referencia

Para copiar um objeto de fato sem referencia você pode atribuir uma propriedade de cada vez desta forma.

O codigo acima cria um novo objeto sem referência a pessoa

Agora o objeto permanece como foi copiado. Podemos alterar tudo, que ele continua o mesmo.

Deep copy

Sua tradução literal é “copia profunda”. Exatamente oque é um algoritimo que realiza uma copia manual sem referencia, de todas a propriedades que são objetos.

Porque se alguma propriedade dele for um objeto tambem… ele continua a ser uma referencia rsrsr.

O codigo cria uma propriedade na referência do objeto contatos em pessoa
WTF??

O que aconteceu na verdade foi bem simples, como a propriedade contato é um objeto, ele apontou o clone.contatos e pessoa.contatos para contatos.
É por esse motivo javascript é uma das linguagens mais performaticas, sempre que você atribui diretamente um objeto, ele não copia, ele criar uma referência. Veja a representação grafica o que aconteceu.

Representação grafica de quando se cria uma propriedade em uma referência mesmo sendo indireta

Soluções

Então, sempre que for copiar um objeto tenho que passar propriedade por propriedade? meio chato né? Temos varias soluções tudo depende do seu tipo de objeto:

Objeto simples

Se estiver usando um objeto simples (apenas propriedades com valores primitivos), ou seja um objeto que não contenha outros objetos ou funções, a solução mais simples é usar:

O assign da es6

O codigo realiza um assign copiado o objeto sem referência

Ou o spread operator tambem da es6

O codigo realiza um destructing do objeto para copialo sem referência

Objeto simples de multiplos niveis

Se estiver usando um objeto que contenha outros objetos uma solução simples é usar os métodos do JSON:

O codigo copia um objeto que contem outros objetos com metodos do JSON

Assim ele tranforma seu objeto em um valor primitivo criando um novo espaço na memória e então apartir deste valor ele cria um novo objeto.

Objeto complexos

Mas a solução masi flexivel e universal é fazer um algorito de deep copy:

O codigo cria um algoritimo de deepcopy para copiar qualquer tipo de objeto

Esta função serve para quase todos os casos.

Conclusão

Copiar objetos no javascript é algo simples mas que pode ser complexo, e acabar criando muitas referencia e causar um memory leak (Ecedex de consumo de memoria), mas no fim oque importa é saber bem oque se faz ;D

--

--