Spread e rest em objetos
1. Introdução ao Spread Operator (...) em Objetos
O operador spread (...) em objetos foi introduzido no ES2018 e permite expandir as propriedades de um objeto em outro contexto. Diferente dos arrays, onde o spread expande elementos individuais, em objetos ele expande pares chave-valor.
const pessoa = { nome: 'João', idade: 30 };
const pessoaCompleta = { ...pessoa, cidade: 'São Paulo' };
console.log(pessoaCompleta); // { nome: 'João', idade: 30, cidade: 'São Paulo' }
A diferença fundamental entre spread em objetos e arrays é que em objetos trabalhamos com propriedades nomeadas, enquanto em arrays trabalhamos com índices numéricos.
// Spread em arrays
const numeros = [1, 2, 3];
const novosNumeros = [...numeros, 4]; // [1, 2, 3, 4]
// Spread em objetos
const config = { tema: 'escuro', fonte: 'Arial' };
const novaConfig = { ...config, idioma: 'pt-BR' }; // { tema: 'escuro', fonte: 'Arial', idioma: 'pt-BR' }
2. Clonagem e Fusão de Objetos com Spread
A clonagem rasa com spread é uma das aplicações mais comuns:
const usuarioOriginal = { nome: 'Maria', email: 'maria@exemplo.com' };
const clone = { ...usuarioOriginal };
console.log(clone); // { nome: 'Maria', email: 'maria@exemplo.com' }
console.log(clone === usuarioOriginal); // false (objetos diferentes)
A fusão de múltiplos objetos permite combinar propriedades de forma elegante:
const defaults = { modo: 'claro', notificacoes: true, idioma: 'en' };
const preferencias = { modo: 'escuro', idioma: 'pt-BR' };
const configuracoesFinais = { ...defaults, ...preferencias };
console.log(configuracoesFinais);
// { modo: 'escuro', notificacoes: true, idioma: 'pt-BR' }
A ordem de precedência é crucial: propriedades de objetos posteriores sobrescrevem as anteriores quando há chaves duplicadas.
3. Spread com Propriedades Dinâmicas e Imutabilidade
O spread é fundamental para manter a imutabilidade em JavaScript moderno. Combinado com computed property names, permite atualizações dinâmicas sem mutação:
const estado = { contador: 0, usuario: null };
function atualizarUsuario(estado, novoUsuario) {
return { ...estado, usuario: novoUsuario };
}
function atualizarPropriedade(estado, chave, valor) {
return { ...estado, [chave]: valor };
}
const novoEstado = atualizarPropriedade(estado, 'contador', 5);
console.log(novoEstado); // { contador: 5, usuario: null }
Em React, essa técnica é essencial para atualizar estado de forma imutável:
// Componente React
function Contador() {
const [state, setState] = React.useState({ valor: 0, cliques: 0 });
const incrementar = () => {
setState(prevState => ({
...prevState,
valor: prevState.valor + 1,
cliques: prevState.cliques + 1
}));
};
return <button onClick={incrementar}>Clique: {state.valor}</button>;
}
4. Rest Operator (...) em Objetos: Desestruturação
O operador rest em objetos permite coletar propriedades restantes durante a desestruturação:
const pessoa = { nome: 'Ana', idade: 25, cidade: 'Rio', profissao: 'engenheira' };
const { nome, idade, ...outrosDados } = pessoa;
console.log(nome); // 'Ana'
console.log(idade); // 25
console.log(outrosDados); // { cidade: 'Rio', profissao: 'engenheira' }
A diferença entre rest em parâmetros de função e desestruturação de objetos:
// Rest em parâmetros de função
function somar(...numeros) {
return numeros.reduce((acc, n) => acc + n, 0);
}
// Rest em desestruturação de objetos
function processarUsuario({ nome, ...resto }) {
console.log(`Processando ${nome}`);
console.log('Dados extras:', resto);
}
5. Casos de Uso Avançados com Spread e Rest
Removendo propriedades específicas usando rest + spread:
function removerPropriedade(objeto, propParaRemover) {
const { [propParaRemover]: _, ...resto } = objeto;
return resto;
}
const usuario = { id: 1, nome: 'Carlos', senha: '123456', email: 'c@exemplo.com' };
const usuarioSemSenha = removerPropriedade(usuario, 'senha');
console.log(usuarioSemSenha); // { id: 1, nome: 'Carlos', email: 'c@exemplo.com' }
Criando objetos com valores padrão:
function criarConfiguracao(opcoes = {}) {
const defaults = { host: 'localhost', porta: 3000, ssl: false };
return { ...defaults, ...opcoes };
}
console.log(criarConfiguracao({ porta: 8080 }));
// { host: 'localhost', porta: 8080, ssl: false }
Extraindo parâmetros nomeados de um objeto de opções:
function conectar({ host, porta, ...configuracoesExtras }) {
console.log(`Conectando a ${host}:${porta}`);
console.log('Configurações extras:', configuracoesExtras);
}
conectar({ host: 'api.exemplo.com', porta: 443, timeout: 5000, retry: 3 });
// Conectando a api.exemplo.com:443
// Configurações extras: { timeout: 5000, retry: 3 }
6. Spread e Rest em Contexto de React
Passando props de forma explícita com spread:
function Card({ titulo, descricao, ...restProps }) {
return (
<div {...restProps}>
<h2>{titulo}</h2>
<p>{descricao}</p>
</div>
);
}
// Uso
<Card
titulo="Meu Card"
descricao="Descrição aqui"
className="card-principal"
onClick={() => alert('Clicou!')}
/>
Sobrescrita de props com spread para valores padrão:
function BotaoCustomizado(props) {
const defaultProps = {
cor: 'azul',
tamanho: 'medio',
desabilitado: false
};
const propsFinais = { ...defaultProps, ...props };
return <button disabled={propsFinais.desabilitado}>{propsFinais.children}</button>;
}
Separando props específicas do restante:
function Input({ label, error, ...inputProps }) {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
{error && <span className="erro">{error}</span>}
</div>
);
}
// Uso
<Input
label="Email"
type="email"
placeholder="Digite seu email"
required
/>
7. Boas Práticas e Cuidados
Limitações da clonagem rasa:
const objetoAninhado = {
usuario: { nome: 'João' },
preferencias: { tema: 'escuro' }
};
const clone = { ...objetoAninhado };
clone.usuario.nome = 'Maria'; // Isso modifica o original também!
console.log(objetoAninhado.usuario.nome); // 'Maria'
Para clonagem profunda, considere JSON.parse(JSON.stringify()) ou bibliotecas como Lodash.
Performance: Evite spreads excessivos em loops ou renderizações frequentes:
// Ruim - criando novo objeto a cada iteração
function processarUsuarios(usuarios) {
return usuarios.map(u => ({ ...u, processado: true }));
}
// Melhor - se precisar de performance máxima
function processarUsuarios(usuarios) {
return usuarios.map(u => Object.assign({}, u, { processado: true }));
}
Compatibilidade: Spread em objetos é suportado desde Node.js 8.6+ e todos os navegadores modernos (Chrome 60+, Firefox 55+, Safari 11.1+, Edge 79+).
8. Conclusão e Próximos Passos
O spread e rest em objetos são ferramentas poderosas que permitem escrever código mais limpo, imutável e expressivo em JavaScript moderno. Dominamos desde clonagem simples até padrões avançados em React.
Para praticar, experimente:
1. Criar uma função que mescla múltiplos objetos ignorando valores null ou undefined
2. Implementar um reducer de estado imutável usando spread
3. Construir um componente React que aceite props dinâmicas usando rest
Esses conceitos se conectam diretamente com desestruturação, programação funcional, gerenciamento de estado em React e padrões de design de componentes.
Referências
- MDN Web Docs: Spread syntax (...) — Documentação oficial da Mozilla sobre spread operator em objetos e arrays
- MDN Web Docs: Destructuring assignment - Rest in objects — Guia completo sobre desestruturação com rest em objetos
- JavaScript.info: Object rest/spread properties — Tutorial detalhado com exemplos práticos de spread e rest em objetos
- React Documentation: State and Lifecycle - Using State Correctly — Documentação oficial do React sobre imutabilidade e atualização de estado
- W3Schools: JavaScript Spread Operator — Referência básica com exemplos didáticos do operador spread
- Stack Overflow: What is the difference between spread and rest operator in JavaScript? — Discussão técnica sobre as diferenças entre spread e rest operators