Arrays são ótimos para listas. Mas como representar uma pessoa, um produto, um pedido — entidades com várias características diferentes? Para isso existem os objetos.
Um objeto é uma coleção de propriedades, onde cada propriedade tem um nome (chave) e um valor. É a estrutura de dados mais fundamental do JavaScript — tanto que a linguagem inteira foi construída em cima dela. Arrays são objetos. Funções são objetos. O próprio document do navegador é um objeto.
Entender objetos profundamente é entender o JavaScript.
Criando objetos
const pessoa = {
nome: "Ana Paula",
idade: 28,
profissao: "Desenvolvedora",
ativa: true,
};
A sintaxe usa chaves {}, com pares de chave: valor separados por vírgula. Isso se chama objeto literal — a forma mais comum de criar objetos.
Acessando propriedades
Há duas formas de acessar valores:
const pessoa = {
nome: "Ana Paula",
idade: 28,
profissao: "Desenvolvedora",
};
// Notação de ponto (use sempre que possível)
console.log(pessoa.nome); // Ana Paula
console.log(pessoa.idade); // 28
// Notação de colchetes (use quando a chave é dinâmica)
const campo = "profissao";
console.log(pessoa[campo]); // Desenvolvedora
console.log(pessoa["nome"]); // Ana Paula
A notação de colchetes é essencial quando você não sabe o nome da propriedade antecipadamente — quando ela vem de uma variável, por exemplo.
Modificando e adicionando propriedades
const produto = {
nome: "Notebook",
preco: 3500,
};
// Modificar propriedade existente
produto.preco = 3200;
console.log(produto.preco); // 3200
// Adicionar nova propriedade
produto.estoque = 15;
console.log(produto.estoque); // 15
// Remover propriedade
delete produto.estoque;
console.log(produto.estoque); // undefined
Propriedades com funções — métodos
Quando uma propriedade de um objeto é uma função, ela é chamada de método:
const calculadora = {
somar: function(a, b) {
return a + b;
},
subtrair: function(a, b) {
return a - b;
},
// Sintaxe moderna de método (shorthand):
multiplicar(a, b) {
return a * b;
},
};
console.log(calculadora.somar(5, 3)); // 8
console.log(calculadora.subtrair(10, 4)); // 6
console.log(calculadora.multiplicar(3, 7)); // 21
A palavra-chave this
Dentro de um método, this se refere ao próprio objeto. É como o objeto falando de si mesmo:
const conta = {
titular: "Carlos",
saldo: 1000,
depositar(valor) {
this.saldo += valor;
console.log(`Depósito de R$ ${valor}. Novo saldo: R$ ${this.saldo}`);
},
sacar(valor) {
if (valor > this.saldo) {
console.log("Saldo insuficiente.");
return;
}
this.saldo -= valor;
console.log(`Saque de R$ ${valor}. Novo saldo: R$ ${this.saldo}`);
},
verSaldo() {
console.log(`${this.titular}, seu saldo é R$ ${this.saldo}`);
},
};
conta.verSaldo(); // Carlos, seu saldo é R$ 1000
conta.depositar(500); // Depósito de R$ 500. Novo saldo: R$ 1500
conta.sacar(200); // Saque de R$ 200. Novo saldo: R$ 1300
conta.sacar(2000); // Saldo insuficiente.
Atenção: this dentro de arrow functions não se refere ao objeto — por isso usamos a sintaxe de função normal nos métodos.
Objetos aninhados
Propriedades de um objeto podem ser outros objetos:
const usuario = {
nome: "Fernanda",
idade: 31,
endereco: {
rua: "Av. Paulista",
numero: 1000,
cidade: "São Paulo",
estado: "SP",
},
contato: {
email: "fernanda@email.com",
telefone: "11 99999-0000",
},
};
console.log(usuario.endereco.cidade); // São Paulo
console.log(usuario.contato.email); // fernanda@email.com
console.log(usuario.endereco.numero); // 1000
Arrays de objetos — o padrão mais comum
Na prática, você vai trabalhar o tempo todo com listas de objetos:
const produtos = [
{ id: 1, nome: "Notebook", preco: 3500, categoria: "eletrônicos" },
{ id: 2, nome: "Mouse", preco: 80, categoria: "eletrônicos" },
{ id: 3, nome: "Cadeira", preco: 900, categoria: "móveis" },
{ id: 4, nome: "Mesa", preco: 700, categoria: "móveis" },
];
// Filtrar por categoria
const eletronicos = produtos.filter(p => p.categoria === "eletrônicos");
console.log(eletronicos);
// Encontrar produto por id
const produto = produtos.find(p => p.id === 3);
console.log(produto.nome); // Cadeira
// Listar nomes de todos os produtos
const nomes = produtos.map(p => p.nome);
console.log(nomes); // ["Notebook", "Mouse", "Cadeira", "Mesa"]
// Total do valor em estoque
const total = produtos.reduce((acc, p) => acc + p.preco, 0);
console.log(`Total: R$ ${total}`); // Total: R$ 5180
Viu como map, filter e reduce do artigo Arrays: criando e manipulando listas se encaixam perfeitamente aqui?
Verificando se uma propriedade existe
const usuario = { nome: "João", idade: 25 };
// Operador in
console.log("nome" in usuario); // true
console.log("email" in usuario); // false
// Optional chaining — evita erros ao acessar propriedades aninhadas
const config = {
tema: {
cor: "azul",
},
};
console.log(config.tema?.cor); // "azul"
console.log(config.idioma?.codigo); // undefined (sem erro!)
// Sem o ?. isso causaria: TypeError: Cannot read properties of undefined
O optional chaining (?.) é um dos recursos mais úteis do JavaScript moderno. Use-o sempre que acessar propriedades que podem não existir.
Iterando sobre objetos
const pessoa = {
nome: "Ricardo",
idade: 35,
cidade: "Curitiba",
};
// for...in — percorre as chaves do objeto
for (const chave in pessoa) {
console.log(`${chave}: ${pessoa[chave]}`);
}
// nome: Ricardo
// idade: 35
// cidade: Curitiba
// Object.keys — array com as chaves
console.log(Object.keys(pessoa));
// ["nome", "idade", "cidade"]
// Object.values — array com os valores
console.log(Object.values(pessoa));
// ["Ricardo", 35, "Curitiba"]
// Object.entries — array de pares [chave, valor]
console.log(Object.entries(pessoa));
// [["nome", "Ricardo"], ["idade", 35], ["cidade", "Curitiba"]]
Object.entries() é especialmente poderoso porque você pode encadear métodos de array sobre ele:
const precos = { notebook: 3500, mouse: 80, teclado: 200 };
// Filtrar itens acima de R$ 100 e exibir formatado
Object.entries(precos)
.filter(([, valor]) => valor > 100)
.forEach(([produto, valor]) => {
console.log(`${produto}: R$ ${valor}`);
});
// notebook: R$ 3500
// teclado: R$ 200
Copiando objetos
Objetos são passados por referência — isso é uma fonte clássica de bugs para iniciantes:
const original = { nome: "Ana", idade: 25 };
// ❌ Isso NÃO cria uma cópia — ambas apontam para o mesmo objeto
const copia = original;
copia.nome = "Maria";
console.log(original.nome); // "Maria" ← o original foi alterado!
Para copiar corretamente, use o spread operator:
const original = { nome: "Ana", idade: 25 };
// ✅ Cria uma cópia independente (shallow copy)
const copia = { ...original };
copia.nome = "Maria";
console.log(original.nome); // "Ana" ← intacto
console.log(copia.nome); // "Maria"
Falaremos mais sobre spread operator no próximo artigo.
Tarefa para você
Modele um sistema simples de biblioteca:
// 1. Crie um array com pelo menos 4 livros
// Cada livro deve ter: titulo, autor, ano, disponivel (boolean), paginas
// 2. Filtre apenas os livros disponíveis
// 3. Encontre todos os livros publicados após 2000
// 4. Exiba o título e autor de cada livro no formato:
// "Dom Casmurro — Machado de Assis"
// 5. Calcule o total de páginas de todos os livros
// Dica: combine map, filter e reduce com seu array de objetos
Ver solução — a biblioteca completa, dos livros ao total de páginas
// ---------------------------------------------------------------
// 1 — o acervo
// ---------------------------------------------------------------
const livros = [
{ titulo: "Dom Casmurro", autor: "Machado de Assis", ano: 1899, disponivel: true, paginas: 256 },
{ titulo: "Torto Arado", autor: "Itamar Vieira Junior", ano: 2019, disponivel: true, paginas: 264 },
{ titulo: "A Hora da Estrela", autor: "Clarice Lispector", ano: 1977, disponivel: false, paginas: 96 },
{ titulo: "O Avesso da Pele", autor: "Jeferson Tenório", ano: 2020, disponivel: true, paginas: 176 },
{ titulo: "Vidas Secas", autor: "Graciliano Ramos", ano: 1938, disponivel: false, paginas: 176 },
];
// ---------------------------------------------------------------
// 2 — só os disponíveis
// ---------------------------------------------------------------
// `livro.disponivel` já é boolean: comparar com === true é redundante.
const disponiveis = livros.filter((livro) => livro.disponivel);
console.log(`${disponiveis.length} de ${livros.length} disponíveis`);
// ---------------------------------------------------------------
// 3 — publicados depois de 2000
// ---------------------------------------------------------------
const recentes = livros.filter((livro) => livro.ano > 2000);
console.log(recentes.map((l) => `${l.titulo} (${l.ano})`));
// [ 'Torto Arado (2019)', 'O Avesso da Pele (2020)' ]
// ---------------------------------------------------------------
// 4 — "Título — Autor"
// ---------------------------------------------------------------
// O travessão é U+2014, não hífen. Copiado errado, a lista fica com
// "Dom Casmurro - Machado" e ninguém entende por que ficou feio.
for (const { titulo, autor } of livros) {
console.log(`${titulo} — ${autor}`);
}
// ---------------------------------------------------------------
// 5 — total de páginas
// ---------------------------------------------------------------
const totalPaginas = livros.reduce((soma, livro) => soma + livro.paginas, 0);
console.log(`Total: ${totalPaginas} páginas`); // Total: 968 páginas
// ---------------------------------------------------------------
// Os três juntos: relatório do acervo
// ---------------------------------------------------------------
function relatorio(acervo) {
const emprestados = acervo.filter((l) => !l.disponivel);
// reduce não serve só para somar: aqui ele agrupa por autor,
// acumulando um objeto em vez de um número.
const porAutor = acervo.reduce((mapa, livro) => {
mapa[livro.autor] = (mapa[livro.autor] ?? 0) + 1;
return mapa;
}, {});
// Math.max com spread em vez de um laço só para achar o maior.
const maisLongo = acervo.reduce((maior, livro) =>
livro.paginas > maior.paginas ? livro : maior
);
return {
total: acervo.length,
emprestados: emprestados.length,
paginas: acervo.reduce((s, l) => s + l.paginas, 0),
mediaPaginas: Math.round(acervo.reduce((s, l) => s + l.paginas, 0) / acervo.length),
autores: Object.keys(porAutor).length,
maisLongo: `${maisLongo.titulo} (${maisLongo.paginas}p)`,
};
}
console.table(relatorio(livros));
// ---------------------------------------------------------------
// O detalhe que morde: ?? não é a mesma coisa que ||
// ---------------------------------------------------------------
// No contador acima, `|| 0` funcionaria por acaso. Em contador que
// pode legitimamente valer 0, não funciona:
const emprestimos = { "Vidas Secas": 0 };
console.log(emprestimos["Vidas Secas"] || 99); // 99 — errado, 0 é falsy
console.log(emprestimos["Vidas Secas"] ?? 99); // 0 — certo, existe
?? só cai no padrão quando o valor é null ou undefined; || cai também com 0, "" e false. Em contador, preço e campo de formulário — onde zero e string vazia são respostas válidas — usar || apaga o dado do usuário.
O que mais custa caro aqui não é a sintaxe, é a referência: atribuir um objeto a outra variável não copia nada, e mexer em um mexe no outro. Daí a importância do spread e do structuredClone quando a cópia precisa ser de verdade, e daí também o cuidado com cópia rasa, que resolve o primeiro nível e deixa os de dentro compartilhados. O ?. trata o problema vizinho — alcançar o que pode não existir sem derrubar a execução três níveis abaixo.
Fontes e Referências
- MDN Web Docs — Objetos: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Working_with_Objects
- MDN Web Docs — Optional Chaining: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Optional_chaining
- MDN Web Docs — Object.entries: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
- JavaScript.info — Objects: https://javascript.info/object
- JavaScript.info — Object methods and this: https://javascript.info/object-methods
- Airbnb JavaScript Style Guide — Objects: https://github.com/airbnb/javascript#objects
Exercícios
Exercício 1
O que a última linha imprime, e por quê? Escreva a correção de uma linha.
const original = { nome: "Ana", idade: 25 };
const copia = original;
copia.nome = "Maria";
console.log(original.nome);
Ver resposta
✓ Resposta: Imprime Maria. A linha const copia = original não copia o objeto — copia a referência, o endereço. As duas variáveis passam a apontar para o mesmo objeto na memória, então mexer em qualquer uma delas altera o mesmo dado. A correção é const copia = { ...original };, que cria um objeto novo com as mesmas propriedades. Note que o const não protege nada aqui: ele impede reatribuir a variável, não modificar o objeto para o qual ela aponta.
Exercício 2
Uma das duas linhas finais imprime Desenvolvedora e a outra imprime outra coisa. Qual é qual?
const pessoa = { nome: "Ana", profissao: "Desenvolvedora" };
const campo = "profissao";
console.log(pessoa[campo]);
console.log(pessoa.campo);
Ver resposta
✓ Resposta: A primeira imprime Desenvolvedora; a segunda, undefined. Nos colchetes, o que está dentro é avaliado: campo vira a string "profissao" e a propriedade é encontrada. Depois do ponto, o nome é literal: pessoa.campo procura uma propriedade chamada exatamente campo, que não existe. Daí a regra do artigo — ponto quando você sabe o nome ao escrever o código, colchetes quando o nome só se conhece em tempo de execução.
Exercício 3
A primeira linha funciona e a segunda quebra. Qual é a mensagem de erro exata, e qual operador a evita?
const config = { tema: { cor: "azul" } };
console.log(config.tema.cor);
console.log(config.idioma.codigo);
Ver resposta
✓ Resposta: A segunda lança TypeError: Cannot read properties of undefined (reading 'codigo'). O problema não é idioma não existir — ler uma propriedade inexistente devolve undefined sem reclamar. O erro é ler codigo de dentro desse undefined. Com o optional chaining, config.idioma?.codigo devolve undefined e a execução segue: o ?. interrompe a corrente assim que encontra undefined ou null, em vez de tentar o próximo acesso.
Exercício 4
O que Object.entries(precos) devolve, e o que significa o [, valor] no filter?
const precos = { notebook: 3500, mouse: 80, teclado: 200 };
console.log(Object.entries(precos).filter(([, valor]) => valor > 100));
Ver resposta
✓ Resposta: Object.entries devolve um array de pares: [["notebook", 3500], ["mouse", 80], ["teclado", 200]] — e é isso que permite usar filter, map e companhia sobre um objeto, que sozinho não tem esses métodos. O resultado do filtro é [["notebook", 3500], ["teclado", 200]]. O [, valor] é desestruturação de array com a primeira posição pulada: a vírgula sozinha descarta a chave e guarda só o segundo item do par.
Exercício 5
Qual é a saída das quatro chamadas, e quanto vale conta.saldo no fim?
const conta = {
titular: "Carlos",
saldo: 1000,
depositar(valor) { this.saldo += valor; },
sacar(valor) {
if (valor > this.saldo) { console.log("Saldo insuficiente."); return; }
this.saldo -= valor;
},
};
conta.depositar(500);
conta.sacar(200);
conta.sacar(2000);
console.log(conta.saldo);
Ver resposta
✓ Resposta: Só a terceira chamada imprime algo — Saldo insuficiente. —, e no fim conta.saldo vale 1300. A conta: 1000 + 500 = 1500, menos 200 = 1300; o saque de 2000 é maior que 1300, então o return abandona o método antes de subtrair. Repare no papel do this: ele é o próprio objeto conta, e é o que permite ao método ler e alterar o saldo sem que o nome da variável externa apareça dentro dele.