Menu

Operador spread y rest en JavaScript (...)

Cómo funciona el operador ... en JavaScript: recoger argumentos con rest, expandir arrays y objetos con spread, y cuándo conviene usar cada uno.

Misma sintaxis, dos trabajos distintos

Los tres puntos ... aparecen por todas partes en el JavaScript moderno, y hacen cosas opuestas según dónde los pongas. Una vez que pillas el patrón, cualquier uso del operador ... en JavaScript cae en uno de estos dos cubos:

  • Rest (agrupar): ...nombre en el lado que recibe junta varios valores en un único array u objeto.
  • Spread (expandir): ...valor en el lado que entrega despliega un array u objeto en sus piezas individuales.

Ese es todo el modelo mental. El resto de esta página son ejemplos de cada uno y los patrones a los que vas a volver una y otra vez.

Parámetros rest en JavaScript: agrupar argumentos

Un parámetro rest dentro de la definición de una función recoge cualquier cantidad de argumentos y los mete en un array de verdad:

index.js
Output
Click Run to see the output here.

nums es un array normal y corriente. Le puedes aplicar .map, .filter, consultar su .length, pasárselo a otra función... en fin, todo lo que hace cualquier array.

Los parámetros rest pueden convivir con parámetros normales, pero el parámetro rest siempre tiene que ir al final:

index.js
Output
Click Run to see the output here.

label se queda con el primer argumento y todo lo que venga después cae en items. Ojo: si pones el parámetro rest en cualquier posición que no sea la última, obtendrás un error de sintaxis.

Parámetros rest vs. el viejo objeto arguments

El código JavaScript antiguo solía apoyarse en una variable mágica llamada arguments dentro de las funciones tradicionales. Parece un array, pero no lo es, así que los métodos de array no funcionan sobre ella directamente. Los parámetros rest vienen a reemplazarla de forma mucho más limpia:

index.js
Output
Click Run to see the output here.

Las arrow functions ni siquiera tienen el objeto arguments, así que los parámetros rest son la única forma de aceptar una cantidad variable de argumentos dentro de ellas. En código nuevo, usa siempre ...args.

El operador spread al llamar funciones

El spread hace justo lo contrario: toma un array y lo desempaqueta en argumentos individuales al momento de llamar a la función.

index.js
Output
Click Run to see the output here.

Math.max recibe números sueltos, no un array. Antes del spread tocaba escribir Math.max.apply(null, nums). Hoy basta con un ... y listo.

Fíjate en un detalle: los mismos tres puntitos ... funcionan como rest en la definición de la función y como spread en la llamada. Es la posición la que decide cuál de los dos es.

Spread en arrays de JavaScript

Usar el operador spread dentro de un array literal te permite copiar o combinar arrays de forma directa:

index.js
Output
Click Run to see the output here.

[...a] te da un array nuevo con los mismos elementos, ideal cuando quieres ordenarlo o modificarlo sin tocar el original:

index.js
Output
Click Run to see the output here.

scores sigue intacto porque .sort se ejecutó sobre la copia. Es una costumbre pequeña, pero te ahorra muchos dolores de cabeza cuando escribes código que no debería tener efectos secundarios raros.

Spread en objetos con literales

El operador spread también funciona con objetos planos: junta sus propiedades en uno nuevo.

index.js
Output
Click Run to see the output here.

Gana la última clave escrita. updates.age pisa a user.age, y city se cuela sin problema. El orden de los spreads determina el resultado — tenlo presente cuando estés combinando valores por defecto con sobrescrituras:

index.js
Output
Click Run to see the output here.

Los valores por defecto van primero, las preferencias del usuario después. Así, el usuario gana en fontSize pero hereda el theme.

La trampa de la copia superficial

El spread copia un solo nivel. Los objetos y arrays anidados siguen compartiéndose entre el original y la copia:

index.js
Output
Click Run to see the output here.

Ambos arrays muestran la nueva etiqueta porque copy.tags y original.tags apuntan al mismo array. El spread no clonó la lista anidada, solo copió la referencia.

Si necesitas una copia profunda de verdad sobre datos planos, tira de structuredClone:

index.js
Output
Click Run to see the output here.

Ahora los dos arrays son independientes. structuredClone viene integrado en los navegadores modernos y en Node, se encarga de las estructuras anidadas y es la opción correcta cuando una copia "superficial" se queda corta.

Parámetros rest en la desestructuración

El rest también funciona en la desestructuración, donde agrupa los elementos o propiedades restantes:

index.js
Output
Click Run to see the output here.

Sacar algunos campos de un objeto y conservar el resto en otro es un patrón muy habitual: aparece cuando pasas props a otro componente, cuando quieres quitar datos sensibles o cuando necesitas generar una versión modificada de un objeto:

index.js
Output
Click Run to see the output here.

password se extrae (y se ignora); safe se queda con todo lo demás. Sin mutaciones, sin copias manuales.

Repaso rápido

  • ...name en una lista de parámetros o en un patrón de destructuring es rest: agrupa.
  • ...value en una llamada a función, en un array literal o en un objeto literal es spread: expande.
  • Las copias con spread son superficiales. Las estructuras anidadas siguen compartiéndose. Usa structuredClone cuando necesites una copia profunda.
  • Los parámetros rest son arrays de verdad — úsalos en lugar de arguments.
  • En los objetos literales, los spreads posteriores sobrescriben a los anteriores; así es como se arma el patrón de valores por defecto con overrides.

Lo que viene: closures

En JavaScript, las funciones no solo reciben argumentos y devuelven valores — también recuerdan el ámbito en el que fueron definidas. Esa memoria se llama closure, y es el mecanismo que está detrás de los callbacks, las factories y casi todos los patrones que verás en la siguiente página.

Preguntas frecuentes

¿Qué diferencia hay entre rest y spread en JavaScript?

Comparten la misma sintaxis ..., pero hacen cosas opuestas. Rest agrupa varios valores en un único array: aparece en la lista de parámetros de una función o al desestructurar. Spread expande un iterable o un objeto en sus elementos: aparece en llamadas a funciones, literales de array y literales de objeto. Regla mental: si el ... está en el lado que recibe, es rest; si está en el lado que entrega, es spread.

¿Cómo funcionan los parámetros rest en una función?

Un parámetro rest como function sum(...nums) recoge todos los argumentos que le pases a la función dentro de un array real llamado nums. Tiene que ir siempre en la última posición de la lista de parámetros. A diferencia del viejo objeto arguments, un parámetro rest sí es un array de verdad, así que puedes usar directamente .map, .filter o .reduce sobre él.

¿El operador spread hace una copia profunda?

No. Spread copia solo un nivel, es una copia superficial (shallow copy). { ...user } te devuelve un objeto nuevo con las mismas claves de primer nivel, pero los objetos y arrays anidados siguen apuntando a la misma referencia. Si necesitas una copia profunda, usa structuredClone(value) o, para datos planos, serializa con JSON.

Aprende a programar con Coddy

COMENZAR