Objetos são estruturas fundamentais em JavaScript, usados para armazenar dados de forma organizada. Nesta aula, vamos explorar técnicas poderosas para manipulá-los: desde a extração de chaves e valores até a clonagem e proteção contra modificações. Compreender esses conceitos é essencial para escrever código robusto e previsível.

Vamos começar com métodos estáticos do objeto Object que nos permitem iterar sobre propriedades, depois veremos como o operador spread simplifica a cópia e mesclagem de objetos. Em seguida, aprenderemos a congelar objetos com Object.freeze e, por fim, a crucial diferença entre cópia rasa e profunda.

Object.keys/values/entries

Os métodos Object.keys(), Object.values() e Object.entries() são ferramentas essenciais para trabalhar com objetos de forma iterativa. Eles retornam arrays contendo, respectivamente, as chaves, os valores ou pares chave-valor de um objeto. Esses métodos são particularmente úteis para percorrer objetos com loops ou para transformar dados.

Exemplo prático: suponha que você tenha um objeto representando um produto e queira listar todas as suas propriedades.

const produto = {
  nome: 'Notebook',
  preco: 3500,
  emEstoque: true
};

console.log(Object.keys(produto));   // ['nome', 'preco', 'emEstoque']
console.log(Object.values(produto)); // ['Notebook', 3500, true]
console.log(Object.entries(produto));
// [['nome', 'Notebook'], ['preco', 3500], ['emEstoque', true]]

Você pode usar Object.entries() com um loop for...of para iterar sobre chaves e valores simultaneamente:

for (const [chave, valor] of Object.entries(produto)) {
  console.log(`${chave}: ${valor}`);
}
// nome: Notebook
// preco: 3500
// emEstoque: true

Spread em objetos

O operador spread (...) foi introduzido no ES2018 para objetos, permitindo copiar propriedades de um objeto para outro de forma concisa. É amplamente usado para clonagem rasa e mesclagem de objetos.

Exemplo básico de clonagem:

const original = { a: 1, b: 2 };
const copia = { ...original };
console.log(copia); // { a: 1, b: 2 }
console.log(copia === original); // false

Spread também permite sobrescrever propriedades ou adicionar novas:

const usuario = { nome: 'João', idade: 30 };
const usuarioAtualizado = { ...usuario, idade: 31, email: 'joao@email.com' };
console.log(usuarioAtualizado);
// { nome: 'João', idade: 31, email: 'joao@email.com' }

Importante: a ordem importa. Propriedades posteriores sobrescrevem as anteriores.

Freeze

Object.freeze() impede que um objeto seja modificado: não é possível adicionar, remover ou alterar propriedades existentes. É útil para criar constantes ou garantir imutabilidade.

const config = { api: 'https://api.exemplo.com', timeout: 5000 };
Object.freeze(config);
config.timeout = 6000; // Ignorado em modo estrito, lança erro em strict mode
console.log(config.timeout); // 5000

No entanto, Object.freeze é raso: se o objeto contiver objetos aninhados, eles ainda podem ser modificados. Para congelamento profundo, é necessário implementar recursão ou usar bibliotecas como deep-freeze.

const dados = { nivel1: { nivel2: 'valor' } };
Object.freeze(dados);
dados.nivel1.nivel2 = 'novo valor'; // Permitido!
console.log(dados.nivel1.nivel2); // 'novo valor'

Cópia rasa vs profunda

Ao copiar objetos, precisamos entender a diferença entre cópia rasa (shallow copy) e cópia profunda (deep copy). Uma cópia rasa duplica apenas as propriedades do primeiro nível; para propriedades que são objetos, a referência é copiada, não o objeto em si. Já a cópia profunda cria uma duplicata independente de toda a hierarquia.

Exemplo de cópia rasa usando spread:

const original = { a: 1, b: { c: 2 } };
const copiaRasa = { ...original };
copiaRasa.b.c = 3;
console.log(original.b.c); // 3 (afetou o original)

Para cópia profunda, uma abordagem comum é usar JSON.parse(JSON.stringify(obj)), mas ela tem limitações (não suporta funções, undefined, símbolos, etc.).

const copiaProfunda = JSON.parse(JSON.stringify(original));
copiaProfunda.b.c = 4;
console.log(original.b.c); // 3 (não afetou)

Alternativas modernas incluem structuredClone() (navegadores e Node.js 17+) e bibliotecas como Lodash (_.cloneDeep).

Boas práticas

  • Prefira imutabilidade sempre que possível: crie novos objetos em vez de modificar os existentes.
  • Use Object.freeze para configs e constantes que não devem ser alteradas.
  • Ao copiar objetos, escolha o método adequado: spread para cópia rasa simples, structuredClone para cópia profunda confiável.
  • Evite JSON.parse(JSON.stringify(obj)) para objetos com funções, datas ou referências circulares.

Referências

Exercícios

  1. Exercício 1: Dado o objeto carro = { marca: 'Fiat', modelo: 'Uno', ano: 2020 }, use Object.values para obter um array com os valores e exiba no console.

    ✓ Resposta:
    const carro = { marca: 'Fiat', modelo: 'Uno', ano: 2020 };
    console.log(Object.values(carro)); // ['Fiat', 'Uno', 2020]
  2. Exercício 2: Use o operador spread para criar um novo objeto carro2 que copie todas as propriedades de carro e altere o ano para 2022.

    ✓ Resposta:
    const carro2 = { ...carro, ano: 2022 };
    console.log(carro2); // { marca: 'Fiat', modelo: 'Uno', ano: 2022 }
  3. Exercício 3: Congele o objeto carro com Object.freeze e tente alterar a propriedade marca. Verifique se a alteração foi ignorada.

    ✓ Resposta:
    Object.freeze(carro);
    carro.marca = 'Chevrolet';
    console.log(carro.marca); // 'Fiat' (alteração ignorada)
  4. Exercício 4: Dado o objeto pessoa = { nome: 'Ana', endereco: { cidade: 'SP', rua: 'A' } }, faça uma cópia rasa usando spread e altere a cidade na cópia. A cidade do objeto original também foi alterada? Explique.

    ✓ Resposta:
    const pessoa = { nome: 'Ana', endereco: { cidade: 'SP', rua: 'A' } };
    const copia = { ...pessoa };
    copia.endereco.cidade = 'RJ';
    console.log(pessoa.endereco.cidade); // 'RJ' (sim, foi alterado porque a cópia é rasa)
  5. Exercício 5: Use JSON.parse(JSON.stringify()) para fazer uma cópia profunda do objeto pessoa. Altere a rua na cópia e verifique se o original permanece inalterado.

    ✓ Resposta:
    const copiaProfunda = JSON.parse(JSON.stringify(pessoa));
    copiaProfunda.endereco.rua = 'B';
    console.log(pessoa.endereco.rua); // 'A' (original não foi alterado)