Saltar al contenido
desestructuracion-y-spread.js · devschool

Desestructuración y spread en JS

Lección 17 de 38 · 9 min de lectura · Actualizado el

En esta lección
  1. Desestructurar arrays
  2. Desestructurar objetos
  3. Desestructurar en los parámetros
  4. Spread: expandir con ...
  5. Copia superficial frente a copia profunda
  6. Rest: recoger lo que sobra
  7. ?. y ?? junto a la desestructuración
  8. Errores frecuentes
  9. Resumen

Los tres puntos (...) y las llaves y corchetes a la izquierda de un = aparecen en casi todo el código moderno: en React, en las respuestas de una API, en las funciones con muchas opciones. Son la desestructuración, el spread y el rest. No hacen nada que no pudieras hacer antes con más líneas, pero hacen el código mucho más corto y claro. En esta lección aprenderás a sacar datos de arrays y objetos, a copiarlos y fusionarlos sin modificar el original, y a entender cuándo una copia es de verdad una copia.

Necesitas tener claros los arrays y los objetos.

Desestructurar arrays

Desestructurar es sacar valores de un array o un objeto y guardarlos en variables en una sola línea. En un array, los valores se asignan por posición:

const podio = ["Ana", "Luis", "Eva", "Marta"];

// Sin desestructuración
const primero = podio[0];
const segundo = podio[1];

// Con desestructuración
const [oro, plata, bronce] = podio;
console.log(oro, plata, bronce); // Ana Luis Eva

Los corchetes a la izquierda del = no crean un array: indican “saca de aquí las posiciones 0, 1 y 2”. Si pides más variables que elementos hay, las que sobran valen undefined.

Saltar elementos

Deja un hueco entre comas:

const [campeona, , tercera] = podio;
console.log(campeona, tercera); // Ana Eva

Valores por defecto

Se usan si la posición no existe (o vale undefined):

const [x = 1, y = 2] = [10];
console.log(x, y); // 10 2

Intercambiar variables

El truco clásico, sin variable auxiliar:

let izquierda = "A";
let derecha = "B";
[izquierda, derecha] = [derecha, izquierda];
console.log(izquierda, derecha); // B A

Un caso real

Separar una fecha en año, mes y día:

const [anio, mes, dia] = "2026-09-30".split("-").map(Number);
console.log(anio, mes, dia); // 2026 9 30

Desestructurar objetos

Con objetos se usan llaves y los valores se asignan por nombre, no por posición. El orden da igual:

const libro = {
  titulo: "Rayuela",
  autor: "Cortázar",
  paginas: 600,
  editorial: { nombre: "Alfaguara", ciudad: "Madrid" },
};

const { autor, titulo } = libro;
console.log(titulo, autor); // Rayuela Cortázar

La variable se llama igual que la propiedad. Si la propiedad no existe, la variable vale undefined.

Renombrar

Si ya tienes una variable con ese nombre o prefieres otro, usa propiedad: nuevoNombre:

const { titulo: nombreLibro } = libro;
console.log(nombreLibro); // Rayuela

Se lee “coge titulo y guárdalo en nombreLibro”. Es fácil confundirlo con un objeto: en la desestructuración, lo de la derecha de los dos puntos es la variable.

Valores por defecto

const { isbn = "sin ISBN", paginas = 0 } = libro;
console.log(isbn, paginas); // sin ISBN 600

paginas existe, así que se ignora el 0. Se puede combinar con renombrar: const { isbn: codigo = "sin ISBN" } = libro;.

Desestructuración anidada

Para sacar algo de un objeto que está dentro de otro, repite la forma:

const { editorial: { ciudad } } = libro;
console.log(ciudad); // Madrid

Ojo: aquí se crea la variable ciudad, pero no editorial. Y si editorial no existiera, daría error. No anides más de dos niveles: se vuelve ilegible.

Desestructurar en los parámetros

Es donde más se usa. Cuando una función recibe un objeto, puedes desestructurarlo en la propia firma:

function ficha({ titulo, autor, anio = "desconocido" }) {
  return `${titulo} (${autor}, ${anio})`;
}

ficha(libro); // "Rayuela (Cortázar, desconocido)"

Para funciones con muchas opciones es ideal, porque quien la llama pasa solo las que quiere, en cualquier orden y con nombre:

function crearBoton({ texto = "Aceptar", color = "azul", tamanio = "normal" } = {}) {
  return `${texto}-${color}-${tamanio}`;
}

crearBoton({ color: "rojo" }); // "Aceptar-rojo-normal"
crearBoton();                  // "Aceptar-azul-normal"

El = {} final es importante: si llamas a la función sin argumentos, desestructurar undefined falla con TypeError: Cannot destructure property 'texto' of 'undefined'.

También funciona en bucles y callbacks:

const usuarios = [
  { nombre: "Ana", edad: 30 },
  { nombre: "Luis", edad: 17 },
];

for (const { nombre, edad } of usuarios) {
  console.log(nombre, edad);
}
// Ana 30
// Luis 17

usuarios.map(({ nombre }) => nombre); // [ 'Ana', 'Luis' ]

for (const [clave, valor] of Object.entries({ a: 1, b: 2 })) {
  console.log(clave, valor); // a 1, b 2
}

Spread: expandir con ...

El operador spread “despliega” los elementos de un array (o las propiedades de un objeto) en otro sitio.

Spread en arrays

const frutas = ["pera", "kiwi"];
const verduras = ["col"];

const compra = [...frutas, "uva", ...verduras];
// [ 'pera', 'kiwi', 'uva', 'col' ]

const copia = [...frutas];
copia.push("mango");
console.log(frutas, copia); // [ 'pera', 'kiwi' ] [ 'pera', 'kiwi', 'mango' ]

También sirve para pasar un array como argumentos sueltos y para convertir en array cualquier cosa recorrible:

Math.max(...[4, 9, 2]);  // 9  (igual que Math.max(4, 9, 2))
[..."hola"];             // [ 'h', 'o', 'l', 'a' ]
[...new Set([1, 1, 2])]; // [ 1, 2 ]

Spread en objetos

Copia las propiedades de un objeto dentro de otro. Si una propiedad se repite, gana la última:

const porDefecto = { tema: "claro", idioma: "es", avisos: true };
const delUsuario = { tema: "oscuro" };

const config = { ...porDefecto, ...delUsuario };
// { tema: 'oscuro', idioma: 'es', avisos: true }

Por eso el orden importa: primero los valores por defecto, después los que deben ganar. Es la forma habitual de “cambiar” una propiedad sin tocar el original:

const reedicion = { ...libro, paginas: 650 };
console.log(reedicion.paginas, libro.paginas); // 650 600

Copia superficial frente a copia profunda

Aquí está la trampa. El spread hace una copia superficial: copia el primer nivel, pero los objetos y arrays de dentro se comparten.

const alumno = { nombre: "Eva", notas: [7, 8] };

const copia = { ...alumno };
copia.nombre = "Otra";   // no afecta al original
copia.notas.push(10);    // ¡sí afecta! es el mismo array

console.log(alumno); // { nombre: 'Eva', notas: [ 7, 8, 10 ] }

copia.notas y alumno.notas apuntan al mismo array. Entenderás el porqué en el apartado de referencias de objetos.

Para una copia profunda (todos los niveles), usa structuredClone, disponible en todos los navegadores modernos y en Node:

const independiente = structuredClone(alumno);
independiente.notas.push(1);
console.log(alumno.notas, independiente.notas); // [ 7, 8, 10 ] [ 7, 8, 10, 1 ]

structuredClone copia objetos, arrays, fechas, Map y Set, pero no funciones: si el objeto tiene métodos, lanza DataCloneError. Verás en código antiguo JSON.parse(JSON.stringify(obj)); funciona peor (pierde fechas, undefined e Infinity), así que prefiere structuredClone.

FormaNivelComparte lo anidado
const b = aNinguno: es el mismo objetoTodo
[...a] / { ...a }SuperficialSí
structuredClone(a)ProfundaNo

Rest: recoger lo que sobra

Los mismos tres puntos, pero en el lado contrario, hacen lo opuesto: recogen varios valores en uno. Se llama rest y siempre va el último.

En desestructuración de arrays, recoge los elementos restantes en un array:

const [ganador, ...resto] = podio;
console.log(ganador, resto); // Ana [ 'Luis', 'Eva', 'Marta' ]

En objetos, recoge las propiedades que no has nombrado. Es la forma limpia de quitar una propiedad sin delete:

const { password, ...publico } = { nombre: "Ana", password: "1234" };
console.log(publico); // { nombre: 'Ana' }

En parámetros, recoge los argumentos en un array (lo viste en funciones avanzadas):

function sumar(...numeros) {
  return numeros.reduce((total, n) => total + n, 0);
}

sumar(1, 2, 3);    // 6
const notas = [5, 8, 6];
sumar(...notas);   // 19: spread al llamar, rest al recibir

¿Cómo distinguirlos? Spread está donde se usan valores (a la derecha del =, dentro de [] o {} que se crean, al llamar a una función). Rest está donde se reciben (a la izquierda del =, en los parámetros).

?. y ?? junto a la desestructuración

El encadenamiento opcional ?. y el operador ?? ya los tienes en operadores. Combinan muy bien con lo que has visto cuando los datos pueden venir incompletos, por ejemplo de una API:

const respuesta = { usuario: { direccion: null } };
const ciudad = respuesta.usuario?.direccion?.ciudad ?? "Sin ciudad";
console.log(ciudad); // Sin ciudad

Recuerda que el valor por defecto de la desestructuración solo actúa con undefined, no con null. Si un dato puede llegar como null, usa ??.

Errores frecuentes

  • Desestructurar undefined o null: const { a } = undefined lanza TypeError. En parámetros, añade = {}.
  • Confundir renombrar con asignar valor: { titulo: nombre } crea la variable nombre, no una propiedad.
  • Creer que spread copia en profundidad: los objetos anidados se comparten; usa structuredClone.
  • Poner el rest en medio: const [...a, b] = lista es un SyntaxError.
  • Olvidar los paréntesis al desestructurar en una variable ya declarada: ({ nombre } = datos);. Sin ellos, las llaves se toman como un bloque.
  • Fusionar en el orden equivocado: { ...delUsuario, ...porDefecto } pisa las preferencias del usuario.

Resumen

SintaxisQué haceEjemplo
const [a, b] = arrSaca por posiciónconst [oro, plata] = podio
const [a, , c] = arrSalta elementosconst [x, , z] = [1, 2, 3]
[a, b] = [b, a]Intercambia
const { a, b } = objSaca por nombreconst { titulo } = libro
const { a: x } = objRenombraconst { titulo: nombre } = libro
const { a = 1 } = objValor por defectoconst { isbn = "-" } = libro
function f({ a, b } = {})Desestructura parámetrosOpciones con nombre
[...arr], { ...obj }Spread: copia superficial{ ...base, tema: "oscuro" }
structuredClone(obj)Copia profunda
const [a, ...resto] = arrRest: recoge lo demásconst { password, ...publico } = u

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

desestructuracion-y-spread.js
Consola

Pon a prueba lo que has aprendido

[JavaScript] ¿Qué muestra la consola?
const [a, , c = 5] = [1, 2];
console.log(a, c);

[JavaScript] ¿Qué muestra la consola?
const a = { nombre: "Eva", notas: [7] };
const b = { ...a };
b.nombre = "Ana";
b.notas.push(9);
console.log(a.nombre, a.notas);

[JavaScript] ¿Qué crea la línea const { titulo: nombre } = libro;?

[JavaScript] ¿Qué muestra la consola?
const base = { tema: "claro", idioma: "es" };
const cfg = { tema: "oscuro", ...base };
console.log(cfg.tema);

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.