Mesma sintaxe, duas funções
As três reticências ... aparecem bastante no JavaScript moderno, e fazem coisas opostas dependendo de onde estão escritas. Depois que você pega o padrão, todo uso de ... cai em um de dois baldes:
- Rest:
...nomedo lado que recebe junta vários valores em um único array ou objeto. - Spread:
...valordo lado que entrega expande um array ou objeto nas suas partes individuais.
Esse é o modelo mental completo. O resto desta página são só exemplos de cada um e os padrões que você vai usar toda hora.
Rest parameters: juntando argumentos
Um rest parameter na definição de uma função junta qualquer quantidade de argumentos em um array de verdade:
nums é um array comum. Dá pra fazer .map, .filter, checar o .length, passar pra outra função — tudo que array faz.
Dá pra misturar rest parameters com parâmetros normais, mas o rest parameter precisa vir sempre por último:
label engole o primeiro argumento; tudo que vier depois cai em items. Colocar o rest parameter em qualquer posição que não seja a última gera erro de sintaxe.
Rest vs. o antigo objeto arguments
Códigos JavaScript mais antigos usam uma variável mágica chamada arguments dentro de funções normais. Ela parece um array, mas não é — então os métodos de array não funcionam direto nela. Os rest parameters resolvem isso de forma muito mais limpa:
As arrow functions nem possuem o objeto arguments, então os rest parameters são a única forma de receber um número variável de argumentos nelas. Em código novo, prefira sempre ...args.
Spread em chamadas de função
O spread faz o caminho inverso: pega um array e o "desempacota" em argumentos individuais na hora da chamada.
Math.max aceita números individuais, não um array. Antes do spread, a gente precisava escrever Math.max.apply(null, nums). Agora é só um ... e pronto.
Repare que o mesmo ... vira rest na definição da função e spread na chamada da função — a posição é que determina qual dos dois é.
Spread em arrays literais
Usar spread dentro de um array literal serve para copiar ou combinar arrays:
[...a] devolve um array novo com os mesmos elementos — perfeito quando você quer ordenar ou modificar sem mexer no original:
scores continua intacto porque o .sort rodou em cima da cópia. É um hábito simples, mas que faz toda a diferença quando você escreve código que não pode ter efeitos colaterais inesperados.
Spread em objetos no JavaScript
O operador spread também funciona com objetos comuns, juntando as propriedades de cada um em um novo objeto:
As chaves que vêm depois vencem. O updates.age sobrescreve o user.age, e o city entra junto de carona. A ordem dos spreads define o resultado — fica de olho nisso quando estiver empilhando valores padrão e sobrescritas:
Defaults primeiro, escolhas do usuário depois. O fontSize definido pelo usuário prevalece, e o theme é herdado.
A armadilha da cópia rasa
O spread copia apenas um nível de profundidade. Objetos e arrays aninhados continuam compartilhados entre o original e a cópia:
Os dois arrays mostram a nova tag porque copy.tags e original.tags são, na prática, o mesmo array. O spread não clonou a lista aninhada — apenas copiou a referência.
Quando você realmente precisa de uma cópia profunda de dados simples, use structuredClone:
Agora os dois arrays são independentes. O structuredClone já vem nativo nos navegadores modernos e no Node, dá conta de estruturas aninhadas e é a escolha certa sempre que uma cópia rasa não resolve.
Rest na desestruturação
O rest também funciona na desestruturação, juntando os elementos ou propriedades que sobraram:
Separar alguns campos e manter o restante em um único objeto é um padrão bem comum quando você precisa repassar props, remover campos sensíveis ou montar versões modificadas de um dado:
password é extraído (e ignorado), enquanto safe fica com todo o resto. Sem mutação e sem ter que copiar na mão.
Recapitulando rapidinho
...nomenuma lista de parâmetros ou num padrão de desestruturação é rest: ele coleta....valornuma chamada de função, array literal ou objeto literal é spread: ele expande.- As cópias feitas com spread são rasas. Estruturas aninhadas continuam compartilhadas. Para cópia profunda, use
structuredClone. - Rest parameters são arrays de verdade — prefira eles em vez de
arguments. - Em objetos literais, os spreads posteriores sobrescrevem os anteriores, e é assim que se monta aquele esquema de valores padrão com overrides.
A seguir: Closures
Funções em JavaScript não apenas recebem entradas e devolvem saídas — elas também lembram do escopo em que foram definidas. Esse "memória" é o que chamamos de closure, e é o mecanismo por trás de callbacks, factories e da maioria dos padrões que você vai encontrar na próxima página.
Perguntas frequentes
Qual a diferença entre rest e spread em JavaScript?
A sintaxe é a mesma (...), mas o papel é oposto. O rest junta vários valores em um único array — aparece na lista de parâmetros de funções e em destructuring. Já o spread expande um iterável ou objeto nos seus elementos — aparece em chamadas de função, literais de array e literais de objeto. Regra prática: se o ... está no lado que recebe, é rest; se está no lado que entrega, é spread.
Como funcionam os rest parameters em uma função?
Um rest parameter como function sum(...nums) agrupa todos os argumentos passados para a função em um array de verdade chamado nums. Ele precisa ser sempre o último parâmetro da lista. Diferente do velho objeto arguments, o rest parameter é um array real, então você pode chamar .map, .filter e .reduce direto nele, sem gambiarra.
O spread faz uma cópia profunda (deep copy) do objeto?
Não. O spread copia apenas um nível — é uma shallow copy. { ...user } gera um novo objeto com as mesmas chaves do topo, mas objetos e arrays aninhados continuam apontando para a mesma referência. Se precisar de cópia profunda, use structuredClone(value) ou, para dados simples, passe pelo JSON.parse(JSON.stringify(...)).