Introdução: por que a simples atribuição é uma armadilha?
\nImagine que você está escrevendo um aplicativo de anotações. O usuário cria um cartão com título e texto, depois quer fazer uma cópia para alterá-lo um pouco. Você escreve:
\nconst original = { title: 'Meu plano', text: 'Comprar leite' };\nconst copy = original;\ncopy.title = 'Novo plano';\nconsole.log(original.title); // 'Novo plano' — ops!\nSituação familiar? Se sim, você encontrou a principal característica dos objetos em JavaScript: eles são passados por referência. A atribuição copy = original não cria um novo objeto, apenas copia a referência para o mesmo espaço de memória. Qualquer alteração em copy reflete em original.
No Stack Overflow, a pergunta "How do I correctly clone a JavaScript object?" acumulou milhares de visualizações e dezenas de respostas. Este é um dos problemas mais comuns para iniciantes e até desenvolvedores experientes. Neste artigo, vamos abordar todas as formas de clonagem de objetos em JavaScript, desde as simples até as avançadas, e descobrir qual abordagem é melhor em cada situação.
\n\nCópia superficial (Shallow copy): rápida e simples
\nA cópia superficial cria um novo objeto, mas para objetos aninhados (arrays, outros objetos) copia apenas as referências. É ideal para estruturas planas, mas perigosa para estruturas profundas.
\n\nMétodo 1: Operador spread (…)
\nA forma mais moderna e legível:
\nconst original = { name: 'Ana', age: 25 };\nconst clone = { ...original };\nclone.age = 30;\nconsole.log(original.age); // 25 — tudo certo!\nPrós: notação curta, funciona com arrays e objetos. Contras: não copia métodos (funções) e propriedades do tipo símbolo.
\n\nMétodo 2: Object.assign()
\nMétodo clássico para copiar objeto sem referências no nível superior:
\nconst source = { a: 1, b: { c: 2 } };\nconst target = Object.assign({}, source);\ntarget.a = 100; // source.a não muda\ntarget.b.c = 999; // source.b.c também muda — shallow!\nEste método é especialmente útil quando você precisa combinar vários objetos: Object.assign({}, obj1, obj2).
Método 3: Loop for…in
\nUma forma antiga, mas ainda funcional. Útil para entender a mecânica:
\nfunction shallowClone(obj) {\n const clone = {};\n for (let key in obj) {\n if (obj.hasOwnProperty(key)) {\n clone[key] = obj[key];\n }\n }\n return clone;\n}\nHoje é raramente usado, mas demonstra bem como funciona a clonagem manual de objetos.
\n\nCópia profunda (Deep copy): quando você precisa de tudo
\nSe o seu objeto contém objetos aninhados, arrays ou datas, a shallow copy vai te decepcionar. Você precisa de clonagem profunda js.
\n\nMétodo 4: JSON.parse + JSON.stringify
\nO "gambiarra" mais popular:
\nconst original = { \n name: 'Pedro', \n address: { city: 'Moscou', street: 'Tverskaya' },\n date: new Date()\n};\nconst clone = JSON.parse(JSON.stringify(original));\nclone.address.city = 'SP';\nconsole.log(original.address.city); // 'Moscou' — ufa!\nMas há ressalvas: este método perde funções, undefined, Symbol, e Date se transforma em string. Para dados simples — ok, para complexos — não.
Método 5: structuredClone() — o novo padrão
\nDesde 2022, o JavaScript possui o método nativo structuredClone:
\nconst original = { \n scores: [10, 20, 30], \n meta: { level: 'easy' },\n date: new Date()\n};\nconst clone = structuredClone(original);\nclone.meta.level = 'hard';\nconsole.log(original.meta.level); // 'easy'\nPrós: suporta Date, Map, Set, ArrayBuffer, referências recursivas. Contras: não suporta funções, elementos DOM, Error. Funciona