El spread expande un objeto dentro de otro
El spread de objetos en JavaScript —esos tres puntos delante de un objeto— copia las propiedades propias y enumerables de ese objeto dentro del literal que lo contiene. Es la forma más corta de clonar, fusionar y generar copias modificadas sin tocar el original.
copy tiene las mismas claves y valores que user, pero es otro objeto distinto. Mutar uno no afecta al otro — al menos en el primer nivel. Ya volveremos sobre ese matiz en un momento.
El modelo mental es este: { ...obj } viene a decir "vuelca las propiedades de obj dentro de este nuevo objeto literal". Todo lo que escribas junto al spread pasa a formar parte del resultado.
Clonar y sobrescribir propiedades en un solo paso
El patrón más habitual consiste en usar el spread sobre un objeto y, a la vez, añadir o sobrescribir algunas propiedades. Como gana la última clave declarada, primero va el spread y después las sobrescrituras:
user queda intacto. updated es un objeto nuevo con la propiedad role sobrescrita. Este patrón de actualización inmutable aparece por todas partes en el JavaScript moderno: actualizadores de estado en React, reducers de Redux y, en general, cualquier código que prefiera no mutar los datos originales.
Si inviertes el orden, el comportamiento cambia por completo:
Aquí role: "guest" va al principio, así que user.role termina pisándolo. Es un truco muy útil cuando quieres definir valores por defecto que luego el objeto que haces spread pueda sobrescribir.
Fusionar objetos en JavaScript
Para fusionar objetos en JavaScript, basta con aplicar el spread de dos (o más) objetos dentro de un nuevo literal. Los objetos que vienen después sobrescriben a los anteriores cuando hay claves repetidas:
theme y fontSize vienen de userPrefs; debug se hereda de defaults. Da igual si son tres objetos o cuatro: la regla es la misma — se lee en orden y gana la última escritura.
Es el reemplazo moderno de Object.assign({}, defaults, userPrefs). Ambas formas hacen lo mismo, pero con spread el código queda más legible y evitas caer en el clásico bug de escribir Object.assign(defaults, userPrefs), que muta defaults sin que te des cuenta.
El spread hace una copia superficial
Aquí es donde mucha gente tropieza. El spread copia únicamente las propiedades del primer nivel del objeto. Si el valor de una propiedad es a su vez otro objeto o un array, lo que se copia es la referencia, no el contenido.
Al cambiar copy.address.city también cambió user.address.city, porque ambos objetos comparten el mismo objeto address. El spread solo nos dio un nuevo envoltorio exterior.
Cuando necesites modificar algo anidado, aplica el spread en cada nivel que quieras cambiar:
Para hacer una copia en profundidad de verdad de cualquier tipo de dato, usa structuredClone(obj). Se encarga de objetos anidados, arrays, fechas, maps y sets, y viene incorporado en cualquier runtime moderno.
Spread vs. rest en JavaScript
Comparten los mismos tres puntitos, pero hacen justo lo contrario. El spread expande; el rest recoge.
La regla práctica: si los ... están antes del = (destructuring), es rest. Si están dentro de un objeto o array literal después del =, es spread.
Eliminar una propiedad de forma inmutable
Combinar rest en el destructuring con el spread operator de JavaScript te da una forma limpia de obtener una copia del objeto sin una de sus claves — sin delete y sin mutar nada:
tempToken queda en su propia variable (que ignoras) y todo lo demás aterriza en safe. El user original queda intacto.
Lo que el spread no copia
Hay algunos matices que conviene tener presentes:
- Las propiedades no enumerables no se copian. La mayoría de las propiedades que creas son enumerables por defecto, pero las definidas con
Object.definePropertyy ciertas propiedades internas no lo son. - El prototipo no se copia.
{ ...instancia }te devuelve un objeto plano, no una instancia de la clase original. Los métodos definidos en el prototipo de la clase no estarán en la copia. - Los getters se ejecutan. Al hacer spread sobre un objeto con un getter, este se invoca una vez y el valor que devuelve se guarda como una propiedad normal en el nuevo objeto.
copy tiene x e y, pero es un objeto plano: distance vive en Point.prototype, y el spread ni lo tocó. Si necesitas clonar una instancia de una clase, lo normal es que la propia clase exponga su método clone.
Lo que viene: métodos de array
El spread es solo una pieza del kit para trabajar con datos inmutables. La otra gran pieza son los métodos de array — map, filter, reduce y compañía —, que devuelven arrays nuevos en lugar de mutar los originales. Eso lo vemos en la siguiente página.
Preguntas frecuentes
¿Qué hace ...obj en JavaScript?
Dentro de un literal de objeto, ...obj copia todas las propiedades propias y enumerables de ese objeto al nuevo. Así, { ...user } te devuelve un objeto nuevo con las mismas claves y valores que user. Es la forma habitual de clonar o fusionar objetos sin mutar los originales.
¿Cómo fusiono dos objetos en JavaScript?
Esparces ambos dentro de un nuevo literal: const merged = { ...a, ...b }. Las propiedades de b pisan a las de a cuando la clave coincide, porque gana la última. Es equivalente a Object.assign({}, a, b), pero queda mucho más legible.
¿El spread de objetos hace una copia profunda?
No. El spread hace una copia superficial (shallow copy): duplica las propiedades de primer nivel, pero los objetos y arrays anidados se siguen compartiendo por referencia. Si modificas copy.address.city, también cambia original.address.city. Para una copia profunda de verdad, usa structuredClone(obj).
¿Cuál es la diferencia entre spread y rest?
Se escriben igual (...), pero hacen justo lo contrario. El spread expande un objeto o array en propiedades o elementos individuales: { ...user }. El rest agrupa lo que sobra en una sola variable: const { name, ...others } = user. El contexto te dice cuál es cuál.