Copiar e modificar objetos em JavaScript nunca é tão simples quanto parece. Compreender como objetos e referências funcionam durante esse processo é essencial para desenvolvedores web e pode economizar horas de depuração. Isso se torna cada vez mais importante quando se trabalha com aplicativos stateful, como os construídos em React ou Vue.
Cópia simples e cópia profunda referem-se a como fazemos cópias de um objeto em JavaScript e que dados são criados na 'cópia'. Neste artigo, vamos aprofundar as distinções entre esses métodos, explorar suas aplicações no mundo real e descobrir as armadilhas potenciais que podem surgir ao usá-los.
O que é 'Cópia Simples'
A cópia simples refere-se ao processo de criar um novo objeto que é uma cópia de um objeto existente, com suas propriedades fazendo referência aos mesmos valores ou objetos do original. Em JavaScript, isso é frequentemente alcançado usando métodos como Object.assign() ou a sintaxe de spread ({...objetoOriginal}). A cópia simples apenas cria uma nova referência para os objetos ou valores existentes e não cria uma cópia profunda, o que significa que objetos aninhados ainda fazem referência aos mesmos objetos, não são duplicados.
Vamos olhar para o exemplo de código a seguir. O objeto recém-criado shallowCopyZoo é criado como uma cópia de zoo através do operador de spread, o que causou algumas consequências não intencionais.
let zoo = { name: "Zoológico Incrível", location: "Melbourne, Austrália", animals: [ { species: "Leão", favoriteTreat: "🥩", }, { species: "Panda", favoriteTreat: "🎋", }, ], }; let shallowCopyZoo = { ...zoo }; shallowCopyZoo.animals[0].favoriteTreat = "🍖"; console.log(zoo.animals[0].favoriteTreat); // "🍖", não "🥩"
Mas vamos analisar o que está realmente em shallowCopyZoo. As propriedades name e location são valores primitivos (string), então seus valores são copiados. No entanto, a propriedade animals é uma matriz de objetos, então a referência a essa matriz é copiada, não a matriz em si.
Você pode testar rapidamente isso (se não acreditar em mim) usando o operador de igualdade estrita (===). Um objeto só é igual a outro objeto se eles se referirem ao mesmo objeto. Observe como a propriedade animals é igual em ambos, mas os objetos em si não são iguais.
console.log(zoo.animals === shallowCopyZoo.animals) // true console.log(zoo === shallowCopyZoo) // false
Isso pode levar a problemas potenciais em bases de código e tornar a vida especialmente difícil ao trabalhar com aplicativos grandes. Modificar um objeto aninhado na cópia simples também afeta o objeto original e qualquer outra cópia simples, pois todos compartilham a mesma referência.
Cópia Profunda
A cópia profunda é uma técnica que cria um novo objeto, que é uma cópia exata de um objeto existente. Isso inclui a cópia de todas as suas propriedades e quaisquer objetos aninhados, em vez de referências. A clonagem profunda é útil quando você precisa de dois objetos separados que não compartilham referências, garantindo que as alterações em um objeto não afetem o outro.
Os programadores frequentemente usam a clonagem profunda ao trabalhar com objetos de estado de aplicativos em aplicativos complexos. Criar um novo objeto de estado sem afetar o estado anterior é crucial para manter a estabilidade do aplicativo e implementar adequadamente a funcionalidade de desfazer e refazer.
Como fazer uma cópia profunda usando JSON.stringify() e JSON.parse()
Uma maneira popular e sem a necessidade de bibliotecas de realizar uma cópia profunda é usar os métodos JSON stringify() e parse() integrados.
O método parse(stringify()) não é perfeito. Por exemplo, tipos de dados especiais como Date serão convertidos em strings e valores indefinidos serão ignorados. Como todas as opções neste artigo, isso deve ser considerado para o seu caso de uso específico.
No código abaixo, criaremos uma função deepCopy que utiliza esses métodos para fazer uma cópia profunda de um objeto. Em seguida, copiaremos o objeto playerProfile e o modificaremos sem afetar o original. Isso destaca o valor da cópia profunda em manter objetos separados sem referências compartilhadas.
const playerProfile = { name: 'Alice', level: 10, achievements: [ { title: 'Fast Learner', emoji: '🚀' }, { title: 'Treasure Hunter', emoji: '💰' } ] }; function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } const clonedProfile = deepCopy(playerProfile); console.log(clonedProfile); /* Saída: { name: 'Alice', level: 10, achievements: [ { title: 'Fast Learner', emoji: '🚀' }, { title: 'Treasure Hunter', emoji: '💰' } ] } */ // Modificar o perfil clonado sem afetar o perfil original clonedProfile.achievements.push({ title: 'Marathon Runner', emoji: '🏃' }); console.log(playerProfile.achievements.length); // Saída: 2 console.log(clonedProfile.achievements.length); // Saída: 3
Este exemplo demonstra como a cópia profunda permite a modificação de um objeto clonado sem afetar o objeto original, mantendo-os separados.
Bibliotecas para Cópia Profunda
Também existem várias bibliotecas de terceiros que oferecem uma solução de cópia profunda.
- A função cloneDeep() da biblioteca Lodash que lida corretamente com referências circulares, funções e objetos especiais.
- A função extend() [deep = true] da biblioteca jQuery.
- A biblioteca immer foi construída com desenvolvedores de React-Redux em mente e fornece ferramentas úteis para a mutação de objetos.
Uma Função de Cópia Profunda em Vanilla JS
Se, por algum motivo, você não deseja usar o objeto JSON ou uma biblioteca de terceiros, também pode criar uma função personalizada de cópia profunda em JavaScript puro que itera recursivamente pelas propriedades do objeto e cria um novo objeto com as mesmas propriedades e valores.
const deepCopy = (obj) => { if (typeof obj !== 'object' || obj === null) { return obj; } const newObj = Array.isArray(obj) ? [] : {}; for (const key in obj) { newObj[key] = deepCopy(obj[key]); } return newObj; } const objetoCopiadoProfundamente = deepCopy(objetoOriginal);
Desvantagens da Cópia Profunda
Embora a cópia profunda ofereça grandes benefícios em termos de precisão dos dados, é recomendável avaliar se a cópia profunda é necessária para cada caso de uso específico. Em algumas situações, a cópia simples ou outras técnicas para gerenciar referências de objetos podem ser mais adequadas, proporcionando melhor desempenho e redução da complexidade.
- Impacto no desempenho: A cópia profunda pode ser computacionalmente cara, especialmente ao lidar com objetos grandes ou complexos. O processo de cópia profunda itera por todas as propriedades aninhadas, o que pode levar uma quantidade significativa de tempo, afetando negativamente o desempenho de sua aplicação.
- Consumo de memória: Criar uma cópia profunda resulta na duplicação de toda a hierarquia de objetos, incluindo todos os objetos aninhados. Isso pode levar a um aumento no uso de memória, o que pode ser problemático, especialmente em ambientes com restrições de memória ou ao lidar com conjuntos de dados grandes.
- Referências circulares: A cópia profunda pode causar problemas quando objetos contêm referências circulares (ou seja, quando um objeto possui uma propriedade que se refere de volta a si mesmo, direta ou indiretamente). Referências circulares podem levar a loops infinitos ou erros de estouro de pilha durante o processo de cópia profunda, e lidar com elas requer lógica adicional para evitar esses problemas.
- Manipulação de funções e objetos especiais: A cópia profunda pode não lidar com funções ou objetos com características especiais (por exemplo, Date, RegExp, elementos DOM) como esperado. Por exemplo, ao copiar profundamente um objeto que contém uma função, a referência da função pode ser copiada, mas o fechamento da função e seu contexto vinculado não serão duplicados. Da mesma forma, objetos com características especiais podem perder suas propriedades e comportamento únicos quando copiados profundamente.
- Complexidade de implementação: Escrever uma função de cópia profunda personalizada pode ser complexo, e os métodos integrados, como JSON.parse(JSON.stringify(obj)), têm limitações, como não lidar adequadamente com funções, referências circulares ou objetos especiais. Embora existam bibliotecas de terceiros, como o _.cloneDeep() do Lodash, que podem lidar com a cópia profunda de maneira mais eficaz, adicionar uma dependência externa para a cópia profunda nem sempre é ideal.
Conclusão
Obrigado por dedicar seu tempo para ler este artigo. A diferenciação entre cópia simples e cópia profunda é surpreendentemente mais complexa do que muitos iniciantes imaginam. Embora haja muitos desafios em cada abordagem, tomar o tempo para revisar e considerar as opções garantirá que sua aplicação e seus dados permaneçam exatamente como você deseja.
Sugestões de cursos
Descubra o caminho para se tornar um especialista em programação web. Aprenda HTML, CSS, JavaScript e os principais frameworks nesta jornada emocionante. Com instrutores experientes e materiais práticos, você desenvolverá habilidades práticas para criar sites impressionantes e aplicativos interativos. Impulsione sua carreira na indústria de tecnologia e abra portas para oportunidades de emprego lucrativas. Garanta sua vaga hoje mesmo e inicie sua jornada para se tornar um desenvolvedor web de sucesso.
Método Para Aprender a Programar do Absoluto ZERO com Node.js, React e React Native.
As tecnologias ensinadas no curso são responsáveis por muitas vagas no mercado de trabalho.
Além da alta demanda, os salários vão de R$47.000,00 até R$197.000,00 anuais tendo empresas que possibilitam o trabalho remoto e até vagas Internacionais.
Para que você possa estar apto a preencher uma dessas vagas eu vou te apresentar o passo a passo para você se tornar um verdadeiro expert nessas tecnologias.
O curso te dará o passo a passo de como criar estruturar de um sistema do zero com Node.js, React e React Native.