Objetos: criando, acessando e modificando propriedades

1. Fundamentos da Criação de Objetos

Objetos em JavaScript são coleções dinâmicas de pares chave-valor. A forma mais comum e recomendada de criá-los é através da sintaxe literal:

const usuario = {
  nome: "Maria",
  idade: 28,
  email: "maria@exemplo.com"
};

Boas práticas de nomenclatura incluem usar camelCase para chaves e evitar caracteres especiais. Embora seja possível usar new Object(), a sintaxe literal é preferida por ser mais concisa e legível:

// Evite
const config = new Object();
config.url = "https://api.exemplo.com";
config.timeout = 5000;

// Prefira
const config = { url: "https://api.exemplo.com", timeout: 5000 };

Objetos aninhados permitem criar estruturas complexas, como dados de usuário ou configurações de API:

const usuarioCompleto = {
  nome: "João",
  endereco: {
    rua: "Av. Paulista",
    numero: 1000,
    cidade: "São Paulo"
  },
  preferencias: {
    tema: "escuro",
    notificacoes: { email: true, push: false }
  }
};

2. Acessando Propriedades de Forma Eficiente

A notação de ponto (obj.prop) é a mais simples e legível, mas possui limitações com nomes dinâmicos ou chaves com caracteres especiais:

const carro = { marca: "Toyota", "ano-fabricacao": 2022 };

console.log(carro.marca); // "Toyota"
// console.log(carro.ano-fabricacao); // Erro! Hífen não permitido
console.log(carro["ano-fabricacao"]); // 2022

A notação de colchetes permite acessar propriedades com variáveis e strings especiais:

const chave = "modelo";
carro[chave] = "Corolla";

const propriedades = ["marca", "modelo", "ano-fabricacao"];
propriedades.forEach(prop => console.log(carro[prop]));

Para acessar propriedades aninhadas com segurança, utilize o encadeamento opcional (?.):

const usuario = { perfil: { nome: "Ana" } };

console.log(usuario.perfil?.nome); // "Ana"
console.log(usuario.contato?.telefone); // undefined (sem erro)
console.log(usuario?.endereco?.cep); // undefined

3. Modificando Propriedades Existentes

A atualização direta é feita por reatribuição simples:

const produto = { nome: "Notebook", preco: 3500 };
produto.preco = 3200; // Atualização direta
produto["em-estoque"] = true;

Ao modificar propriedades aninhadas, tenha cuidado com mutação acidental:

const configSistema = {
  banco: { host: "localhost", porta: 5432 },
  cache: { ativo: true, tempo: 3600 }
};

// Modificação segura de propriedade aninhada
configSistema.banco.porta = 5433;

O método Object.assign() é útil para atualizações parciais:

const defaults = { tema: "claro", idioma: "pt-BR", timeout: 3000 };
const userConfig = { tema: "escuro" };

const configFinal = Object.assign({}, defaults, userConfig);
console.log(configFinal); // { tema: "escuro", idioma: "pt-BR", timeout: 3000 }

4. Adicionando e Removendo Propriedades

Adicionar novas chaves dinamicamente é simples:

const pedido = { id: 123, itens: ["camiseta"] };
pedido.status = "pendente";
pedido["data-criacao"] = new Date().toISOString();

Para remover propriedades, utilize o operador delete:

const cliente = { nome: "Carlos", cpf: "123.456.789-00", senha: "secreta" };
delete cliente.senha;
console.log(cliente); // { nome: "Carlos", cpf: "123.456.789-00" }

Boas práticas: evite deletar propriedades em objetos frequentemente acessados (como em loops), pois isso pode degradar a performance. Prefira criar novos objetos sem as propriedades indesejadas.

5. Propriedades Computadas e Dinâmicas

Propriedades computadas permitem criar chaves dinâmicas na declaração:

const prefixo = "usuario_";
const id = 42;

const registro = {
  [prefixo + id]: "João Silva",
  [`timestamp_${Date.now()}`]: new Date().toISOString()
};

Em tempo de execução, é possível atualizar propriedades com nomes gerados dinamicamente:

function atualizarConfig(chave, valor) {
  const config = { tema: "claro" };
  config[chave] = valor;
  return config;
}

console.log(atualizarConfig("idioma", "en")); // { tema: "claro", idioma: "en" }

Exemplo prático em React: mapeando dados de formulário para objeto:

function handleFormSubmit(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const dados = {};

  for (let [chave, valor] of formData.entries()) {
    dados[chave] = valor;
  }

  console.log(dados); // { nome: "Maria", email: "maria@exemplo.com" }
}

6. Imutabilidade e Cópia de Objetos

Objetos são passados por referência, não por valor:

const original = { valor: 10 };
const copiaReferencia = original;
copiaReferencia.valor = 20;
console.log(original.valor); // 20 - foi modificado!

Para cópia rasa, use spread operator ou Object.assign():

const original = { a: 1, b: { c: 2 } };
const copiaRasa = { ...original };
copiaRasa.a = 100;
copiaRasa.b.c = 200;

console.log(original.b.c); // 200 - objetos aninhados ainda compartilham referência

Para cópia profunda, utilize JSON.parse(JSON.stringify(obj)) ou bibliotecas como lodash:

const profundo = JSON.parse(JSON.stringify(original));
profundo.b.c = 999;
console.log(original.b.c); // 200 - inalterado

// Com lodash: const copiaProfunda = _.cloneDeep(original);

7. Iterando e Listando Propriedades

Diferentes métodos para iterar sobre propriedades:

const dados = { nome: "Ana", idade: 30, cidade: "Rio" };

// for...in (inclui propriedades do protótipo)
for (let chave in dados) {
  console.log(`${chave}: ${dados[chave]}`);
}

// Object.keys() - apenas próprias
Object.keys(dados).forEach(chave => console.log(chave));

// Object.values()
Object.values(dados).forEach(valor => console.log(valor));

// Object.entries() - útil para React
Object.entries(dados).forEach(([chave, valor]) => {
  console.log(`${chave}: ${valor}`);
});

Verificando existência de propriedades:

const obj = { nome: "Pedro" };

console.log(obj.hasOwnProperty("nome")); // true
console.log("nome" in obj); // true
console.log(obj.hasOwnProperty("toString")); // false (herdado)
console.log("toString" in obj); // true (herdado)

Exemplo React: renderizando listas com .map() e .entries():

function TabelaUsuario({ usuario }) {
  return (
    <table>
      <tbody>
        {Object.entries(usuario).map(([chave, valor]) => (
          <tr key={chave}>
            <td>{chave}</td>
            <td>{String(valor)}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

8. Boas Práticas em Projetos Node.js e React

Desestruturação de objetos em parâmetros de funções (especialmente em componentes React):

// Em vez de:
function Saudacao(props) {
  return <h1>Olá, {props.nome}</h1>;
}

// Prefira:
function Saudacao({ nome, idade }) {
  return <h1>Olá, {nome} - {idade} anos</h1>;
}

Propriedades padrão com valores fallback:

function ConfigCard({ tema = "claro", idioma = "pt-BR", timeout = 3000 }) {
  return (
    <div className={`card card-${tema}`}>
      <p>Idioma: {idioma}</p>
      <p>Timeout: {timeout}ms</p>
    </div>
  );
}

Cuidados com mutação em estado React (sempre use imutabilidade):

// ERRADO - muta o estado diretamente
function atualizarUsuario(usuario, novaCidade) {
  usuario.cidade = novaCidade; // ❌ Muta o objeto original
  return usuario;
}

// CORRETO - cria novo objeto
function atualizarUsuario(usuario, novaCidade) {
  return { ...usuario, cidade: novaCidade }; // ✅ Imutável
}

// Exemplo com estado React
const [usuario, setUsuario] = useState({ nome: "João", cidade: "SP" });

function handleCidadeChange(novaCidade) {
  setUsuario(prev => ({ ...prev, cidade: novaCidade }));
}

Referências