En el capítulo de arrays y objetos del Nivel 2 aprendiste la base: const { nombre, rol } = heroe
desempaqueta propiedades de un objeto, y const [primero, segundo] = alineacion hace lo mismo
con un array por posición. Si necesitas refrescar lo básico, vuelve a ese capítulo.
Aquí vamos a los patrones que de verdad se usan a diario en código de empresa: bajar un
nivel (anidado), destruturar en la firma de una función, intercambiar variables en una línea,
y los patrones con for...of y Object.entries. Con el dataset de siempre: héroes del
Overwatch Team Builder.
Los héroes tienen
nombre,rol,partidas,victoriasy un objeto anidadostats(conrangoymvp). El objetoequipoagrupa metadatos y unaalineacion(array de nombres).
Recordatorio compacto#
Si necesitas el destructuring básico de un vistazo antes de empezar:
// Partimos de un héroe de ejemplo.
const heroe = { nombre: "Tracer", rol: "Daño", partidas: 120, victorias: 78 };
const alineacion = ["Mercy", "Reinhardt", "Tracer"];
// Objeto: una variable por clave (el orden no importa).
// equivale a const nombre = heroe.nombre; const rol = heroe.rol;
const { nombre, rol } = heroe;
// Tracer (Daño)
console.log(nombre + " (" + rol + ")");
// Renombrar: la clave 'nombre' va a la variable 'nombreHeroe'.
// la variable 'nombre' NO existe; usamos 'nombreHeroe'
const { nombre: nombreHeroe } = heroe;
// Tracer
console.log(nombreHeroe);
// Valor por defecto: si la clave falta, se usa el valor que pongas.
// rango no existe en heroe → usa 'Sin clasificar'
const { rango = "Sin clasificar" } = heroe;
// Sin clasificar
console.log(rango);
// Array: por posición (el orden sí importa).
// primero = índice 0, segundo = índice 1
const [primero, segundo] = alineacion;
// Mercy
console.log(primero);
// Saltar posición: coma sola para ignorar el índice 0.
// ignora el índice 0, coge el 1
const [, suplente] = alineacion;
// Reinhardt
console.log(suplente);Con eso claro, entramos en lo nuevo.
Destructuring anidado: bajar un nivel#
Cuando el dato vive dentro de otro objeto, puedes desempaquetar en profundidad replicando
la forma del objeto a la izquierda del =.
// stats es un objeto dentro del héroe.
const heroe = {
nombre: "Tracer",
stats: { rango: "Diamante", mvp: 12 },
};
// Baja un nivel: de heroe saca stats, y de stats saca rango y mvp.
// La forma de la izquierda imita la del objeto.
const {
nombre,
stats: { rango, mvp },
} = heroe;
// nombre → 'Tracer' rango → 'Diamante' mvp → 12
// OJO: 'stats' NO queda como variable; solo es el camino para llegar a rango y mvp.
// Tracer — Diamante (12 MVP)
console.log(nombre + " — " + rango + " (" + mvp + " MVP)");Si el objeto anidado podría no existir, combina el anidado con un valor por defecto del objeto entero, para no reventar:
// sin stats
const heroe = { nombre: "Genji" };
// El = {} de stats evita el error si stats falta; el = 'Sin clasificar' cubre rango.
const { stats: { rango = "Sin clasificar" } = {} } = heroe;
// Sin clasificar (no peta aunque no haya stats)
console.log(rango);Destructuring en los parámetros de una función#
El uso que más vas a ver. Si una función recibe un objeto, puedes destructurarlo en la propia firma: la función declara qué campos usa y su cuerpo queda limpio.
// La función recibe el héroe entero, pero la firma solo nombra lo que usa.
// Dentro no hay un solo heroe.: trabajamos con nombre, rol, victorias, partidas.
function fichaHeroe({ nombre, rol, victorias, partidas }) {
// winrate en %
const winrate = ((victorias / partidas) * 100).toFixed(1);
return nombre + " (" + rol + ") — " + winrate + "%";
}
const tracer = { nombre: "Tracer", rol: "Daño", partidas: 120, victorias: 78 };
// Tracer (Daño) — 65.0%
console.log(fichaHeroe(tracer));Esto es exactamente lo que verás en React: function Ficha({ nombre, rol }) { ... }. Lo que
allí llaman “desestructurar las props” es esto mismo. Aprenderlo ahora, con JavaScript a
secas, es quitarte ese obstáculo antes de tiempo.
Intercambio de variables sin temporal#
Un truco clásico: intercambiar el valor de dos variables normalmente necesita una tercera variable de paso. Con destructuring, se hace en una línea:
// queremos que capitan sea 'Tracer' y suplente sea 'Mercy'
let capitan = 'Mercy';
let suplente = 'Tracer';
// Intercambio con destructuring: el array de la derecha evalúa ANTES de asignar.
// [suplente, capitan] crea un array con los valores actuales: ['Tracer', 'Mercy']
// luego los asigna a capitan y suplente por posición
[capitan, suplente] = [suplente, capitan];
// capitan → 'Tracer' suplente → 'Mercy'
console.log('Nuevo capitán: ' + capitan);
console.log('Nuevo suplente: ' + suplente);Sin destructuring haría falta const tmp = capitan; capitan = suplente; suplente = tmp;.
Son tres líneas en vez de una, y la variable tmp no aporta nada al significado.
Destructuring con for...of y Object.entries#
Ya conoces el bucle for...of y Object.entries del capítulo de arrays y objetos.
Combinados con destructuring quedan mucho más limpios:
// array de héroes de ejemplo
const heroes = [
{ nombre: 'Tracer', rol: 'Daño', partidas: 120, victorias: 78 },
{ nombre: 'Reinhardt', rol: 'Tanque', partidas: 90, victorias: 51 },
{ nombre: 'Mercy', rol: 'Apoyo', partidas: 200, victorias: 130 },
];
// Destructuring directamente en la cabecera del for...of.
// En cada iteración, heroe se desempaqueta: nombre y victorias están listos.
for (const { nombre, victorias, partidas } of heroes) {
// no hace falta heroe.nombre, heroe.victorias, etc.
console.log(nombre + ': ' + victorias + '/' + partidas);
}Con Object.entries y un objeto de estadísticas:
// objeto de estadísticas del equipo
const resumen = { victorias: 259, partidas: 400, derrotas: 141 };
// Object.entries devuelve pares [clave, valor]; los destructuramos en el for.
// clave y valor listos en cada vuelta, sin acceso por índice ni por nombre
for (const [clave, valor] of Object.entries(resumen)) {
// imprime 'victorias: 259', 'partidas: 400', etc.
console.log(clave + ': ' + valor);
}Estos dos patrones (for...of con objeto y for...of con Object.entries) aparecen
constantemente cuando procesas listas de datos: los reconocerás de los array methods del Nivel 2
y los volverás a ver en React al recorrer props y estados.
Pruébalo tú#
Edita el código y pulsa Ejecutar (o Ctrl+Enter) para ver la consola. Empieza por añadir
partidas al destructuring de la cabecera (const { nombre, region }) y loguéalo. Luego prueba a
cambiar el bucle for...of para usar destructuring en la cabecera del bucle.
Comprueba lo que sabes#
Pregunta 1 de 6
Tienes const heroe = { nombre: "Tracer", rol: "Daño" }. ¿Qué hace const { nombre } = heroe?
Tu turno#
Leerlo no es lo mismo que saber escribirlo. Resuélvelo aquí mismo: edita solucion.js, muestra
por la consola la cabecera del equipo y una ficha por héroe. El salto de nivel está en cómo uses el
destructuring: anidado para el rango, en los parámetros para la ficha, y con for...of para
el recorrido. Cuando lo tengas (o si te atascas), despliega las soluciones y fíjate en el
salto de un nivel al siguiente.
Ejercicio · en esta página
Fichas de héroes con destructuring
A partir de la lista de héroes y del objeto equipo, muestra por la consola la cabecera del equipo y una ficha por héroe (nombre, rol, winrate y rango), usando destructuring en vez de repetir heroe. por todas partes. Si un héroe no trae rango, muestra "Sin clasificar".
Paso 1: Que funcione
- La cabecera muestra nombre y región del equipo.
- Cada ficha muestra nombre, rol, winrate y rango.
- Todo se ve en la consola (vale acceder con punto).
Paso 2: Que esté pulido
- Destructuras en los parámetros de la función.
- Destructuring anidado para sacar el rango de stats.
- Un valor por defecto cubre al héroe que no trae rango.
- El winrate se muestra formateado como porcentaje.
Paso 3: Que sea excelente
- Funciones puras: el cálculo del winrate vive aislado de la presentación.
- Combinas destructuring en parámetros, anidado y por posición (el capitán de la alineación).
- Usas template literals y separas el cálculo del render.
Ver soluciones
// ════════════════════════════════════════════════════════════════════════════
// NIVEL OK — "que funcione"
//
// Resuelve el problema con lo mínimo: un destructuring de objeto sencillo para
// la cabecera y, para cada héroe, acceso con punto (heroe.nombre, heroe.rol…).
// Funciona y muestra los datos correctos, que es el primer requisito.
//
// Sus límites (los pule el nivel Mejor):
// - El winrate se muestra como 0.65 en bruto, no como 65.0%.
// - El rango se lee con heroe.stats.rango: si un héroe no trae rango, sale
// "undefined" en la consola en vez de un texto decente.
// - Repite heroe. por todas partes dentro de la ficha.
// ════════════════════════════════════════════════════════════════════════════
// Copia autocontenida de los datos para ejecutar esta solución suelta.
const heroes = [
{
nombre: "Tracer",
rol: "Daño",
partidas: 120,
victorias: 78,
stats: { rango: "Diamante", mvp: 12 },
},
{
nombre: "Reinhardt",
rol: "Tanque",
partidas: 90,
victorias: 51,
stats: { rango: "Platino", mvp: 7 },
},
{
nombre: "Mercy",
rol: "Apoyo",
partidas: 200,
victorias: 130,
stats: { rango: "Maestro", mvp: 21 },
},
{
nombre: "Genji",
rol: "Daño",
partidas: 150,
victorias: 72,
stats: { mvp: 9 },
},
{
nombre: "Ana",
rol: "Apoyo",
partidas: 110,
victorias: 66,
stats: { rango: "Diamante", mvp: 14 },
},
{
nombre: "Winston",
rol: "Tanque",
partidas: 80,
victorias: 38,
stats: { rango: "Oro", mvp: 4 },
},
];
const equipo = {
nombre: "Los Vengadores de King's Row",
region: "Europa",
alineacion: ["Mercy", "Reinhardt", "Tracer"],
};
// ─── 1) Cabecera del equipo ─────────────────────────────────────────────────
function cabeceraEquipo(equipo) {
// Destructuring de objeto: sacamos nombre y region en una línea.
// equivale a equipo.nombre y equipo.region
const { nombre, region } = equipo;
// los usamos por su nombre
console.log("Equipo: " + nombre);
console.log("Región: " + region);
}
// ─── 2) Ficha de un héroe ───────────────────────────────────────────────────
function fichaHeroe(heroe) {
// Acceso con punto, repitiendo heroe. en cada campo (funciona, pero verboso).
// winrate en bruto: 0.65
const winrate = heroe.victorias / heroe.partidas;
// nombre del héroe
console.log("--- " + heroe.nombre + " ---");
// su rol
console.log("Rol: " + heroe.rol);
// se ve 0.65, sin formatear
console.log("Winrate: " + winrate);
// si no hay rango, sale undefined
console.log("Rango: " + heroe.stats.rango);
}
// ─── 3) Mostrar ─────────────────────────────────────────────────────────────
function mostrar() {
cabeceraEquipo(equipo);
for (let i = 0; i < heroes.length; i++) {
// ficha de cada héroe
fichaHeroe(heroes[i]);
}
}
mostrar(); Por qué este nivel
- Resuelve con un destructuring básico para la cabecera y acceso con punto (heroe.nombre, heroe.stats.rango) para las fichas.
- Funciona y muestra los datos, que es el primer requisito.
- Sus límites: el winrate sale en bruto (0.65), el héroe sin rango muestra "undefined" y se repite heroe. en cada campo.
// ════════════════════════════════════════════════════════════════════════════
// NIVEL MEJOR — "que esté pulido"
//
// Por qué mejora a OK:
// - Destructuring directamente en los PARÁMETROS de la función: la firma
// fichaHeroe({ nombre, rol, ... }) dice qué campos usa con solo leerla.
// Dentro ya no se repite heroe. por todas partes.
// - Valor por defecto en el destructuring: rango = "Sin clasificar". El héroe
// que no trae rango (Genji) deja de mostrar "undefined" y muestra un texto
// decente. Lo resuelve la sintaxis, sin un if.
// - El winrate se formatea a porcentaje legible (65.0%) en vez de 0.65.
//
// Qué todavía deja para Excelente: la lógica del winrate se puede aislar en
// su propia función pura, y se puede añadir el capitán de la alineación con
// destructuring de array.
// ════════════════════════════════════════════════════════════════════════════
// Copia autocontenida de los datos para ejecutar esta solución suelta.
const heroes = [
{
nombre: "Tracer",
rol: "Daño",
partidas: 120,
victorias: 78,
stats: { rango: "Diamante", mvp: 12 },
},
{
nombre: "Reinhardt",
rol: "Tanque",
partidas: 90,
victorias: 51,
stats: { rango: "Platino", mvp: 7 },
},
{
nombre: "Mercy",
rol: "Apoyo",
partidas: 200,
victorias: 130,
stats: { rango: "Maestro", mvp: 21 },
},
{
nombre: "Genji",
rol: "Daño",
partidas: 150,
victorias: 72,
stats: { mvp: 9 },
},
{
nombre: "Ana",
rol: "Apoyo",
partidas: 110,
victorias: 66,
stats: { rango: "Diamante", mvp: 14 },
},
{
nombre: "Winston",
rol: "Tanque",
partidas: 80,
victorias: 38,
stats: { rango: "Oro", mvp: 4 },
},
];
const equipo = {
nombre: "Los Vengadores de King's Row",
region: "Europa",
alineacion: ["Mercy", "Reinhardt", "Tracer"],
};
// ─── 1) Cabecera del equipo ─────────────────────────────────────────────────
// Destructuring directamente en el parámetro: la función recibe el objeto
// entero pero solo se queda con nombre y region.
function cabeceraEquipo({ nombre, region }) {
console.log("Equipo: " + nombre);
console.log("Región: " + region);
}
// ─── 2) Ficha de un héroe ───────────────────────────────────────────────────
// Destructuring en el parámetro, con destructuring ANIDADO para sacar rango de
// stats, y un VALOR POR DEFECTO para el héroe que no lo trae.
function fichaHeroe({
nombre,
rol,
partidas,
victorias,
// anidado + defecto; {} por si faltara stats
stats: { rango = "Sin clasificar" } = {},
}) {
// a porcentaje
const winrate = ((victorias / partidas) * 100).toFixed(1);
// ya no repetimos heroe.
console.log("--- " + nombre + " ---");
console.log("Rol: " + rol);
// 65.0% en vez de 0.65
console.log("Winrate: " + winrate + "%");
// "Sin clasificar" si no venía
console.log("Rango: " + rango);
}
// ─── 3) Mostrar ─────────────────────────────────────────────────────────────
function mostrar() {
cabeceraEquipo(equipo);
// map transforma cada héroe llamando a fichaHeroe con sus datos
heroes.map(fichaHeroe);
}
mostrar(); Por qué es mejor que el anterior
- Destructura en los PARÁMETROS: la firma fichaHeroe({ nombre, rol, ... }) dice qué campos usa y dentro ya no se repite heroe.
- Destructuring anidado para sacar rango de stats, con un valor por defecto ("Sin clasificar") que resuelve el héroe sin rango sin un if.
- Formatea el winrate a porcentaje legible (65.0%).
- Todavía concatena el HTML con +; eso lo pule el nivel Excelente con template literals.
// ════════════════════════════════════════════════════════════════════════════
// NIVEL EXCELENTE — "óptimo"
//
// Por qué mejora a Mejor:
// - Cada función es PURA y reutilizable: recibe lo que necesita y devuelve
// siempre lo mismo para la misma entrada, sin tocar nada de fuera.
// - El cálculo (winrate) vive aislado en su propia función y el formateo en
// otra: la ficha solo PRESENTA, no calcula. Separar cálculo de presentación
// es lo que mantiene el mostrar legible cuando la app crece.
// - Destructuring en TODOS los frentes y de forma combinada: en parámetros,
// anidado con defecto, por posición en el array de la alineación, y dentro
// del propio forEach del mostrar. El código se lee como los datos que describe.
// - Template literals en lugar de concatenar con +: cada línea se lee de un
// vistazo.
//
// Resultado: añadir un campo a la ficha es tocar un solo sitio, y ninguna
// función esconde efectos colaterales.
// ════════════════════════════════════════════════════════════════════════════
// Copia autocontenida de los datos para ejecutar esta solución suelta.
const heroes = [
{
nombre: "Tracer",
rol: "Daño",
partidas: 120,
victorias: 78,
stats: { rango: "Diamante", mvp: 12 },
},
{
nombre: "Reinhardt",
rol: "Tanque",
partidas: 90,
victorias: 51,
stats: { rango: "Platino", mvp: 7 },
},
{
nombre: "Mercy",
rol: "Apoyo",
partidas: 200,
victorias: 130,
stats: { rango: "Maestro", mvp: 21 },
},
{
nombre: "Genji",
rol: "Daño",
partidas: 150,
victorias: 72,
stats: { mvp: 9 },
},
{
nombre: "Ana",
rol: "Apoyo",
partidas: 110,
victorias: 66,
stats: { rango: "Diamante", mvp: 14 },
},
{
nombre: "Winston",
rol: "Tanque",
partidas: 80,
victorias: 38,
stats: { rango: "Oro", mvp: 4 },
},
];
const equipo = {
nombre: "Los Vengadores de King's Row",
region: "Europa",
alineacion: ["Mercy", "Reinhardt", "Tracer"],
};
// ─── Funciones puras de apoyo ───────────────────────────────────────────────
// Winrate (0..1) a partir de un héroe. Pura. Destructuring en el parámetro:
// solo necesita partidas y victorias, y así lo declara. Protege la división.
function winrateDe({ partidas, victorias }) {
return partidas === 0 ? 0 : victorias / partidas;
}
// Formatea un winrate (0..1) a porcentaje con un decimal. Pura.
function formatearWinrate(winrate) {
return `${(winrate * 100).toFixed(1)}%`;
}
// ─── 1) Cabecera del equipo ─────────────────────────────────────────────────
// Destructuring en el parámetro para los metadatos y, por POSICIÓN, sobre la
// alineación: el primero es el capitán.
function cabeceraEquipo({ nombre, region, alineacion }) {
// destructuring de array: el primero del array
const [capitan] = alineacion;
console.log(`Equipo: ${nombre}`);
console.log(`Región: ${region} · Capitán: ${capitan}`);
}
// ─── 2) Ficha de un héroe ───────────────────────────────────────────────────
// Destructuring en el parámetro, anidado para rango (con defecto) y reutilizando
// las funciones puras. La ficha solo presenta: no calcula el winrate a mano.
function fichaHeroe(heroe) {
const {
nombre,
rol,
// anidado + defecto + {} de respaldo
stats: { rango = "Sin clasificar" } = {},
} = heroe;
// cálculo aislado, fuera de la presentación
const winrate = formatearWinrate(winrateDe(heroe));
console.log(`--- ${nombre} ---`);
console.log(`Rol: ${rol} · Rango: ${rango}`);
console.log(`Winrate: ${winrate}`);
}
// ─── 3) Mostrar ─────────────────────────────────────────────────────────────
// El mostrar solo orquesta y presenta. Nada de cálculos aquí.
function mostrar() {
cabeceraEquipo(equipo);
heroes.forEach(fichaHeroe);
}
mostrar(); Por qué es mejor que el anterior
- Funciones puras y reutilizables: el cálculo del winrate y su formateo viven aislados; la ficha solo presenta.
- Destructuring combinado en todos los frentes: en parámetros, anidado con defecto, por posición en la alineación (saca el capitán) y dentro del propio map.
- Template literals en lugar de concatenar con +: el HTML se lee de un vistazo.
- Separa cálculo de presentación: añadir un campo a la ficha es tocar un solo sitio.