Voltar
JavaScriptArtigo · 16 de mai. de 2026 · 12 min

jonathanjuliani

Spread e Rest em JavaScript: arrays, objetos e os gotchas de cópia rasa

Spread operator e rest em JavaScript: como funcionam em arrays, objetos e funções, e os gotchas de cópia rasa que todo dev encontra.

Ilustração do spread operator e rest parameters em JavaScript com arrays e objetos

Você faz const copia = [...original], modifica a cópia, e o array original muda junto. Ou clona um objeto com spread e descobre que uma propriedade aninhada ainda é referência compartilhada. O spread parece mais simples do que é — e os erros que ele esconde são os mais difíceis de debugar porque não jogam exceção nenhuma.

Spread e rest usam a mesma sintaxe (...) com propósitos opostos: spread expande um iterável nos seus elementos, rest coleta elementos separados num array. Entender quando cada um entra em ação — e onde a cópia rasa te pega — é o que este post cobre.

O que você vai aprender:

  • Spread em arrays: clonar, combinar e espalhar como argumento
  • Spread em objetos: substituindo Object.assign de vez
  • Rest parameters: coletando argumentos variádicos com elegância
  • Os gotchas de cópia rasa e como fazer deep copy quando precisar

Pré-requisitos: arrays, objetos e funções básicas em JavaScript. Se quiser ver spread em objetos no contexto das features do ES2018 — onde ele foi introduzido — o post ECMAScript ES7 ao ES10 tem a visão geral de onde cada feature veio.


Spread em arrays: clonar, combinar e espalhar argumentos

A sintaxe ... antes de um array o expande nos seus elementos individuais. O caso mais comum é criar uma cópia superficial:

código
const original = [1, 2, 3];
const copia = [...original];
 
copia.push(4);
 
console.log(original); // [1, 2, 3] — não foi afetado
console.log(copia);    // [1, 2, 3, 4]

Para combinar arrays, spread é mais legível que concat:

código
const frutas = ['maçã', 'banana'];
const verduras = ['cenoura', 'alface'];
 
const mercado = [...frutas, ...verduras];
// ['maçã', 'banana', 'cenoura', 'alface']
 
// você também pode inserir elementos avulsos no meio
const tudo = ['pão', ...frutas, 'leite', ...verduras];
// ['pão', 'maçã', 'banana', 'leite', 'cenoura', 'alface']

Outra aplicação direta: espalhar um array como argumentos de função. Antes do spread, você usava .apply():

código
const numeros = [3, 1, 4, 1, 5, 9];
 
// antes
Math.max.apply(null, numeros); // 9
 
// com spread
Math.max(...numeros); // 9 — muito mais limpo

Funciona com qualquer função que aceite múltiplos argumentos:

código
function somar(a, b, c) {
  return a + b + c;
}
 
const valores = [10, 20, 30];
somar(...valores); // 60

Spread em objetos: chega de Object.assign

Spread em objetos foi introduzido no ES2018 e resolve o que Object.assign resolvia com menos verbosidade.

Clonar um objeto:

código
const usuario = { nome: 'Ana', idade: 28 };
const copia = { ...usuario };
 
copia.nome = 'Bia';
 
console.log(usuario.nome); // 'Ana' — não foi afetado
console.log(copia.nome);   // 'Bia'

Mesclar objetos — quando há chave duplicada, o último vence:

código
const padrao = { tema: 'claro', idioma: 'pt-BR', notificacoes: true };
const preferencias = { tema: 'escuro', tamanhoFonte: 16 };
 
const config = { ...padrao, ...preferencias };
// { tema: 'escuro', idioma: 'pt-BR', notificacoes: true, tamanhoFonte: 16 }

Sobrescrever propriedades específicas sem mutar o original — padrão muito comum em Redux e gerenciamento de estado imutável:

código
function atualizarUsuario(usuario, alteracoes) {
  return { ...usuario, ...alteracoes }; // retorna novo objeto, não muta o original
}
 
const novo = atualizarUsuario(usuario, { idade: 29 });

"Tá Jon, e qual a diferença pro Object.assign mesmo?"

Object.assign muta o primeiro argumento:

código
const resultado = Object.assign(padrao, preferencias);
// padrao foi mutado — resultado e padrao são o mesmo objeto

Com spread, você sempre recebe um objeto novo. A mutação acidental de Object.assign é a razão pela qual o spread em objetos virou padrão. Se quiser usar Object.assign sem mutar, você passa um objeto vazio como primeiro argumento: Object.assign({}, a, b) — mas nesse caso o spread é mais legível.


Rest parameters: o spread ao contrário

Rest parameters coletam os argumentos restantes de uma chamada de função num único array. É o ... no lado receptor, não no chamador:

código
function somar(...numeros) {
  return numeros.reduce((total, n) => total + n, 0);
}
 
somar(1, 2, 3);       // 6
somar(10, 20, 30, 40); // 100

Você pode combinar parâmetros normais com rest — mas rest tem que ser o último:

código
function log(nivel, ...mensagens) {
  // nivel é o primeiro argumento
  // mensagens é um array com o resto
  mensagens.forEach(msg => console.log(`[${nivel}]`, msg));
}
 
log('INFO', 'servidor iniciado', 'porta 3000');
// [INFO] servidor iniciado
// [INFO] porta 3000

A diferença entre rest e o objeto arguments:

código
function comArguments() {
  console.log(arguments);        // object Arguments — não é array de verdade
  console.log([...arguments]);   // array, mas gambiarra
}
 
function comRest(...args) {
  console.log(args);             // array de verdade — tem .map, .filter, tudo
}

arguments não existe em arrow functions, não é um array real e não funciona com desestruturação. Rest parameters resolve todos esses problemas.

"Tá Jon, quando uso rest e quando uso spread?"

Fácil de lembrar: rest está na definição da função (coletando o que chega), spread está na chamada (expandindo o que você manda). São operações inversas:

código
// rest — na assinatura
function juntar(...partes) { return partes.join('-'); }
 
// spread — na chamada
const palavras = ['bora', 'ver', 'como'];
juntar(...palavras); // 'bora-ver-como'

Gotchas: cópia rasa, objetos aninhados e spread em strings

Cópia rasa (shallow copy)

Spread copia apenas o primeiro nível. Se o array ou objeto contém referências — outros objetos, arrays aninhados — essas referências são compartilhadas, não clonadas:

[IMAGEM: diagrama mostrando dois objetos com spread — propriedades primitivas são copiadas por valor, mas propriedades objeto apontam para a mesma referência na memória | alt: "Diagrama de cópia rasa com spread em JavaScript: valores primitivos copiados, objetos aninhados compartilham referência"]

código
const original = {
  nome: 'Ana',
  endereco: { cidade: 'SP', rua: 'Av. Paulista' }, // objeto aninhado
};
 
const copia = { ...original };
 
copia.nome = 'Bia';             // ok — primitivo, cópia independente
copia.endereco.cidade = 'RJ';   // PERIGO — mutou o objeto original também
 
console.log(original.nome);           // 'Ana' — ok
console.log(original.endereco.cidade); // 'RJ' — ops

[IMAGEM: screenshot do console do navegador mostrando original.endereco.cidade como 'RJ' após mutação via copia | alt: "Console do navegador mostrando que o spread de objeto aninhado compartilha referência — original é mutado ao modificar a cópia"]

O mesmo acontece com arrays de objetos:

código
const usuarios = [{ nome: 'Ana' }, { nome: 'Bia' }];
const copia = [...usuarios];
 
copia[0].nome = 'Carlos';
 
console.log(usuarios[0].nome); // 'Carlos' — o objeto dentro ainda é referência compartilhada

"Tá Jon, mas então o spread é inútil pra cópia de verdade?"

Não — spread é perfeito pra estruturas rasas, que é a maioria dos casos. O problema é usar spread esperando deep copy quando a estrutura tem aninhamento.

Quando você precisa de uma cópia profunda, tem duas opções principais:

structuredClone (moderno, Node.js 17+ e browsers modernos) — é a forma correta:

código
const copia = structuredClone(original); // clone profundo, sem gambiarra

JSON.parse(JSON.stringify(...)) (funciona em mais ambientes, mas tem limitações):

código
const copia = JSON.parse(JSON.stringify(original));
// perda de: undefined, functions, Date vira string, Set/Map viram {}

Use structuredClone se o ambiente suportar. Só caia no JSON.parse/stringify se precisar de compatibilidade ampla e souber as limitações.

Spread em strings

Uma curiosidade útil: spread funciona em qualquer iterável, incluindo strings:

código
const letras = [...'hello']; // ['h', 'e', 'l', 'l', 'o']
 
// útil pra converter string em array de caracteres
const chars = [...'JavaScript'];
// ['J', 'a', 'v', 'a', 'S', 'c', 'r', 'i', 'p', 't']

Diferente de split(''), o spread lida corretamente com caracteres Unicode de múltiplos bytes (emojis, caracteres de línguas como árabe e japonês):

código
'😀🎉'.split('');  // ['', '', '', ''] — quebra os bytes, não os emojis
[...'😀🎉'];       // ['😀', '🎉'] — correto

TL;DR

UsoSintaxeResultado
Clonar array[...arr]Novo array, primeiro nível copiado
Combinar arrays[...a, ...b]Novo array com elementos de ambos
Espalhar em funçãofn(...arr)Passa cada elemento como argumento
Clonar objeto{...obj}Novo objeto, primeiro nível copiado
Mesclar objetos{...a, ...b}Último vence em chave duplicada
Rest parametersfunction f(...args)args é array com os argumentos
Deep copystructuredClone(obj)Clone profundo real
Spread em string[...'texto']Array de caracteres (Unicode correto)
Primitivossempre copiados por valorAlteração na cópia não afeta original
Objetos aninhadoscopiados por referênciaAlteração afeta o original — use structuredClone

Próximos passos

Spread em objetos entrou no ES2018 — se quiser ver o contexto completo de onde ele veio junto com Object.fromEntries, flat() e o for await...of, o ECMAScript ES7 ao ES10: as features que você usa sem saber de onde vieram cobre tudo isso com exemplos práticos.

E se você usa spread pra montar payloads de requisições assíncronas, entender Promises de verdade ajuda a fechar o ciclo: Promises em JavaScript: do zero ao async/await.


[PREENCHER: adicionar CTA personalizado aqui — pode ser algo como "Qual gotcha de spread te pegou em produção? Aquele de cópia rasa pega todo mundo pelo menos uma vez. Me fala nos comentários — e se viu alguma coisa errada aqui, corrige sem cerimônia :D"]

Inscreva-se no Ledger da Engenharia

Receba notas de arquitetura e performance na sua caixa de entrada. Mesma lista do convite—inscreva-se aqui quando quiser.

Sem spam. Sem APIs de terceiros. Apenas eu enviando updates.

O Ledger da Engenharia

Transmissoes quinzenais sobre arquitetura, performance e engenharia aplicada. Inscreva-se a partir de qualquer artigo—sem spam.