DevPath · Aprenda a programar ESPTEN

Programação funcional

Cópia rasa vs profunda (shallow vs deep)

A ilusão da cópia rasa

Quando você copia um objeto com o spread ({ ...obj }) ou com Object.assign({}, obj), o JavaScript copia apenas o primeiro nível. Se alguma propriedade for, por sua vez, um objeto ou um array, o que se copia é a referência, não o seu conteúdo. A cópia e o original compartilham esse objeto aninhado.

Pense em uma fotocópia de um álbum de fotos: a fotocópia tem a sua própria capa, mas em vez de copiar as fotos cola um post-it que diz "as fotos estão no álbum original". Se alguém riscar uma foto, ela aparece riscada em ambos.

const original = {
  nome: "Ana",
  endereco: { cidade: "Madri" },
};

const copia = { ...original };
copia.nome = "Bia";               // afeta SOMENTE a cópia (primeiro nível)
copia.endereco.cidade = "Bilbao"; // afeta TAMBÉM o original!

console.log(original.nome);            // "Ana"    (certo)
console.log(original.endereco.cidade); // "Bilbao" (bug!)

nome é uma string (primitivo) e foi copiado de verdade. Mas endereco é um objeto: ambos o compartilham. Mudar um muda o outro.

Cópia profunda (deep copy)

Uma cópia profunda clona recursivamente todos os níveis, de modo que a cópia seja totalmente independente do original. Há três caminhos comuns:

1. structuredClone (a forma moderna)

É uma função nativa do navegador e do Node moderno que clona em profundidade:

const copia = structuredClone(original);
copia.endereco.cidade = "Bilbao";
console.log(original.endereco.cidade); // "Madri" (intacto)

Suporta objetos, arrays, Map, Set, datas... mas não funções.

2. JSON (para dados simples)

Converter para texto e voltar cria uma cópia profunda em uma linha:

const copia = JSON.parse(JSON.stringify(original));

É prático, mas só serve para dados "JSON-puros": perde funções, undefined, datas (viram string), Map/Set, etc.

3. Recursão na mão

Percorrer a estrutura e clonar cada nível. É o que as duas opções anteriores fazem por dentro:

function clonar(valor) {
  if (Array.isArray(valor)) return valor.map(clonar);
  if (valor && typeof valor === "object") {
    const saida = {};
    for (const chave of Object.keys(valor)) saida[chave] = clonar(valor[chave]);
    return saida;
  }
  return valor; // primitivos: retornados tal como estão
}

Congelar para impedir mutações: Object.freeze

Se você quiser garantir que um objeto não seja modificado, Object.freeze o torna somente leitura: qualquer tentativa de mudança é ignorada (ou lança erro em modo estrito).

const config = Object.freeze({ tema: "escuro" });
config.tema = "claro";      // ignorado
console.log(config.tema);   // "escuro"

Cuidado: Object.freeze também é raso. Congela o primeiro nível, mas os objetos aninhados continuam mutáveis a menos que você os congele também (um "freeze profundo" recursivo).

Exemplos

O bug da cópia rasa

const original = { nivel: 1, dados: { pontos: [10, 20] } };
const copia = { ...original };
copia.dados.pontos.push(30); // muta o array COMPARTILHADO
console.log("original:", original.dados.pontos); // [10, 20, 30] contaminado!
console.log("copia:   ", copia.dados.pontos);    // [10, 20, 30]

Cópia profunda com structuredClone

const original = { nivel: 1, dados: { pontos: [10, 20] } };
const copia = structuredClone(original);
copia.dados.pontos.push(30);
console.log("original:", original.dados.pontos); // [10, 20] intacto
console.log("copia:   ", copia.dados.pontos);    // [10, 20, 30]

Object.freeze impede mutar

const ajustes = Object.freeze({ volume: 5 });
ajustes.volume = 11; // ignorado silenciosamente
console.log(ajustes.volume); // 5
Coloque isto em prática

O DevPath é um curso prático: aqui você lê a teoria; no app você a coloca em prática com exercícios que rodam de verdade, offline.

Comece grátis no app →
← Composição e curryingVer o módulo →