JS 15: Objetos Avanzados

Destructuring, Spread, Rest y trucos modernos.

1. Objetos Literales: Entendiendo el Concepto

¿Qué es realmente un Objeto? (Analogía)

Imagina que tienes una Mochila.

Hasta ahora, si querías guardar datos, usabas variables sueltas (`let colorMochila`, `let pesoMochila`, `let bolsillos`). Pero si tienes 50 mochilas, ¡es un caos de variables!

Un Objeto es esa mochila completa. Es una "super-caja" donde guardas todo lo relacionado con una cosa específica, organizada por etiquetas.

  • Las Propiedades son las características (color, tamaño, marca).
  • Los Métodos son las cosas que puede HACER (abrirse, cerrarse, romperse).
¿Para qué sirve en la vida real?
En desarrollo web, usamos objetos para TODO:
  • Un Usuario: Tiene nombre, email, contraseña, foto (propiedades) y puede iniciar sesión o salir (métodos).
  • Un Botón: Tiene color, tamaño, texto (propiedades) y reacciona al click (método).
  • Un Producto de Tienda: Tiene precio, stock, descripción, imágenes.
Sin objetos, tendrías miles de variables sueltas imposibles de manejar.

Sintaxis: Creando tu primer objeto

Se usan las llaves {}. Dentro ponemos pares de clave: valor.

Sintaxis Básica

// Crear un objeto vacío

const persona = {};



// Objeto con propiedades

const coche = {

  marca: 'Toyota',

  modelo: 'Corolla',

  año: 2023,

  color: 'rojo'

};



console.log(coche.marca);  // "Toyota"

console.log(coche['año']); // 2023

Propiedades Calculadas (ES6+)

Puedes usar expresiones como nombres de propiedades:

const propiedad = 'nombre';

const valor = 'Juan';



const usuario = {

  [propiedad]: valor,        // nombre: "Juan"

  ['edad' + 'Usuario']: 25   // edadUsuario: 25

};



console.log(usuario.nombre);       // "Juan"

console.log(usuario.edadUsuario);  // 25

Shorthand Properties (Atajo de Propiedades)

const nombre = 'Ana';

const edad = 30;



// ? Forma antigua

const persona1 = {

  nombre: nombre,

  edad: edad

};



// ? ES6: Si la variable tiene el mismo nombre, puedes omitir

const persona2 = {

  nombre,  // Equivale a nombre: nombre

  edad     // Equivale a edad: edad

};



console.log(persona2); // { nombre: "Ana", edad: 30 }

Comparación: Array vs Objeto

Array (El Tren)
Los datos van en vagones numerados (0, 1, 2...). El orden importa.
let tren = ["Rojo", "Grande"];
// ¿Qué es "Rojo"? ¿Color o nombre?
// No se sabe sin contexto.
Objeto (La Caja de Herramientas)
Cada herramienta tiene su sitio específico con una etiqueta. El orden NO importa.
let caja = { color: "Rojo", tamaño: "Grande" };
// Aquí sabemos exactamente qué es cada dato.

Mezclando todo: Propiedades y Métodos a la vez

Puedes (y debes) definir qué ES el objeto y qué HACE al mismo tiempo.

const superheroe = {
  // --- PROPIEDADES (Lo que ES) ---
  nombre: 'Iron Man',
  poder: 'Tecnología',
  vida: 100,

  // --- MÉTODOS (Lo que HACE) ---
  saludar() {
    return "¡Hola! Soy " + this.nombre;
  },

  atacar() {
    this.vida -= 10; // Puede cambiar sus propias propiedades
    return "¡Rayos Repulsores! Vida restante: " + this.vida;
  }
};

console.log(superheroe.nombre);   // "Iron Man"
console.log(superheroe.saludar()); // "¡Hola! Soy Iron Man"
console.log(superheroe.atacar());  // "¡Rayos Repulsores! Vida restante: 90"

🤔 ¿Y si quiero crear a Spiderman, Superman, y Batman?

¡Exacto! Copiar y pegar este código 3 veces sería terrible.

Los Objetos Literales (los de este módulo) son para cosas únicas (TU configuración, TU usuario actual).

Para crear muchos objetos iguales (Fábrica de Superhéroes), usamos CLASES.
👉 Ir al Módulo 09: Programación Orientada a Objetos (Clases) para aprender a hacer "moldes".

?? Importante: Los arrow functions NO tienen su propio this. Si necesitas usar this dentro de un método, usa la sintaxis tradicional o shorthand.

2. Acceso y Modificación de Propiedades

Dot Notation vs Bracket Notation

const usuario = {

  nombre: 'Carlos',

  'nombre completo': 'Carlos García',

  edad: 28

};



// Dot notation (punto)

console.log(usuario.nombre);  // "Carlos"



// Bracket notation (corchetes) - necesario si:

// 1. La propiedad tiene espacios

console.log(usuario['nombre completo']); // "Carlos García"



// 2. La propiedad está en una variable

const prop = 'edad';

console.log(usuario[prop]); // 28



// 3. La propiedad se calcula dinámicamente

const dato = 'nom';

console.log(usuario[dato + 'bre']); // "Carlos"

Añadir, Modificar y Eliminar

const persona = { nombre: 'Laura' };



// Añadir propiedad

persona.edad = 25;

persona['ciudad'] = 'Madrid';



// Modificar

persona.nombre = 'Laura Martínez';



// Eliminar

delete persona.ciudad;



console.log(persona); // { nombre: "Laura Martínez", edad: 25 }

Verificar Existencia de Propiedades

const coche = { marca: 'Ford', año: 2020 };



// in operator

console.log('marca' in coche);   // true

console.log('color' in coche);   // false



// hasOwnProperty (no incluye herencia)

console.log(coche.hasOwnProperty('marca')); // true



// Verificar si es undefined

console.log(coche.color !== undefined); // false

3. Destructuring de Objetos ?

El destructuring es una sintaxis que permite "desempaquetar" valores de objetos (o arrays) en variables individuales.

Sintaxis Básica

const persona = {

  nombre: 'María',

  edad: 32,

  ciudad: 'Barcelona'

};



// ? Forma antigua

const nombre = persona.nombre;

const edad = persona.edad;

const ciudad = persona.ciudad;



// ? Destructuring (ES6)

const { nombre, edad, ciudad } = persona;



console.log(nombre);  // "María"

console.log(edad);    // 32

console.log(ciudad);  // "Barcelona"

Renombrar Variables

const usuario = {

  nombre: 'Pedro',

  edad: 40

};



// Renombrar: { propiedadObjeto: nuevoNombre }

const { nombre: nombreUsuario, edad: años } = usuario;



console.log(nombreUsuario); // "Pedro"

console.log(años);          // 40

// console.log(nombre);     // ? Error: nombre is not defined

Valores por Defecto

const config = {

  tema: 'oscuro'

  // idioma no existe

};



// Si la propiedad no existe, usa el valor por defecto

const { tema, idioma = 'español' } = config;



console.log(tema);   // "oscuro"

console.log(idioma); // "español" (valor por defecto)

Destructuring Anidado

const empresa = {

  nombre: 'TechCorp',

  direccion: {

    calle: 'Gran Vía',

    numero: 123,

    ciudad: 'Madrid'

  }

};



// Destructuring anidado

const {

  nombre,

  direccion: { ciudad, numero }

} = empresa;



console.log(nombre);  // "TechCorp"

console.log(ciudad);  // "Madrid"

console.log(numero);  // 123

// console.log(direccion); // ? Error: direccion no está definida

Destructuring en Parámetros de Función

// ? Forma antigua

function mostrarUsuario(usuario) {

  console.log(usuario.nombre + ' tiene ' + usuario.edad + ' años');

}



// ? Con destructuring

function mostrarUsuarioModerno({ nombre, edad }) {

  console.log(`${nombre} tiene ${edad} años`);

}



const user = { nombre: 'Sara', edad: 28 };

mostrarUsuarioModerno(user); // "Sara tiene 28 años"



// Con valores por defecto

function crearUsuario({ nombre, rol = 'usuario' }) {

  return { nombre, rol };

}



console.log(crearUsuario({ nombre: 'Luis' }));

// { nombre: "Luis", rol: "usuario" }

4. Spread y Rest Operators (...)

Spread Operator (Expandir)

El operador ... "expande" un objeto en sus propiedades individuales:

// Copiar objetos

const original = { a: 1, b: 2 };

const copia = { ...original };



console.log(copia); // { a: 1, b: 2 }

console.log(copia === original); // false (son diferentes objetos)



// Combinar objetos

const datosBasicos = { nombre: 'Ana', edad: 25 };

const contacto = { email: 'ana@mail.com', telefono: '666555444' };



const usuarioCompleto = {

  ...datosBasicos,

  ...contacto,

  activo: true

};



console.log(usuarioCompleto);

// { nombre: "Ana", edad: 25, email: "ana@mail.com", telefono: "666555444", activo: true }

Sobrescribir Propiedades

const config = {

  tema: 'claro',

  idioma: 'es',

  notificaciones: true

};



// Si hay propiedades duplicadas, la última gana

const nuevaConfig = {

  ...config,

  tema: 'oscuro',  // Sobrescribe tema

  volumen: 80      // Nueva propiedad

};



console.log(nuevaConfig);

// { tema: "oscuro", idioma: "es", notificaciones: true, volumen: 80 }

Rest Operator (Recoger)

En destructuring, ... recoge las propiedades restantes:

const usuario = {

  id: 1,

  nombre: 'Carlos',

  email: 'carlos@mail.com',

  edad: 30,

  ciudad: 'Valencia'

};



// "resto" recogerá todas las propiedades excepto id y nombre

const { id, nombre, ...resto } = usuario;



console.log(id);     // 1

console.log(nombre); // "Carlos"

console.log(resto);

// { email: "carlos@mail.com", edad: 30, ciudad: "Valencia" }

Caso de Uso Real: Eliminar Propiedades

const producto = {

  id: 123,

  nombre: 'Laptop',

  precio: 999,

  stock: 5,

  __interno: 'no enviar'

};



// Eliminar __interno sin mutarlo

const { __interno, ...productoLimpio } = producto;



console.log(productoLimpio);

// { id: 123, nombre: "Laptop", precio: 999, stock: 5 }

5. Optional Chaining (?.) - ES2020

El optional chaining permite acceder a propiedades anidadas sin causar errores si alguna parte es null o undefined.

Problema sin Optional Chaining

const usuario = {

  nombre: 'Pedro',

  direccion: {

    ciudad: 'Madrid'

    // calle no existe

  }

};



// ? Esto funciona

console.log(usuario.direccion.ciudad); // "Madrid"



// ? Esto causa ERROR

// console.log(usuario.contacto.telefono);

// Error: Cannot read property 'telefono' of undefined



// Forma antigua de evitar el error

const telefono = usuario.contacto && usuario.contacto.telefono;

console.log(telefono); // undefined (sin error)

Solución con Optional Chaining (?)

const usuario = {
  nombre: 'Laura',
  direccion: {
    ciudad: 'Barcelona'
  }
};

// ❌ Si 'direccion' no existiera, esto daría error sin el ?.
console.log(usuario.direccion?.ciudad); // "Barcelona"
console.log(usuario.contacto?.email);     // undefined (sin error)

Nullish Coalescing (??) - ES2020

A diferencia de || (que falla si el valor es 0 o ""), el operador ?? solo devuelve el valor de la derecha si el de la izquierda es exactamente null o undefined.

let stock = 0;

// Usando || (Mal para números)
let valor1 = stock || 10; // 10 (porque 0 es "falso")

// Usando ?? (Correcto)
let valor2 = stock ?? 10; // 0 (porque 0 es un valor válido)

let nombre = null;
let usuario = nombre ?? "Invitado"; // "Invitado"
// ? Si alguna parte es null/undefined, devuelve undefined const telefono = usuario.contacto?.telefono; console.log(telefono); // undefined (sin error) const calle = usuario.direccion?.calle; console.log(calle); // undefined const ciudad = usuario.direccion?.ciudad; console.log(ciudad); // "Barcelona"

Con Funciones

const api = {

  obtenerDatos() {

    return { total: 100 };

  }

};



// Si el método existe, lo llama

console.log(api.obtenerDatos?.()); // { total: 100 }



// Si el método NO existe, devuelve undefined

console.log(api.enviarDatos?.()); // undefined (sin error)

Con Arrays

const datos = {

  usuarios: ['Ana', 'Luis', 'Carlos']

};



console.log(datos.usuarios?.[0]);  // "Ana"

console.log(datos.productos?.[0]); // undefined (sin error)

6. Nullish Coalescing (??) - ES2020

El operador ?? devuelve el valor de la derecha SOLO si el valor de la izquierda es null o undefined.

Diferencia con OR (||)

const config = {

  volumen: 0,

  notificaciones: false,

  tema: null

};



// ? Problema con ||

const vol1 = config.volumen || 50;

console.log(vol1); // 50 (incorrecto! 0 es falsy pero es válido)



const notif1 = config.notificaciones || true;

console.log(notif1); // true (incorrecto! false es válido)



// ? Solución con ??

const vol2 = config.volumen ?? 50;

console.log(vol2); // 0 (correcto! solo null/undefined se reemplazan)



const notif2 = config.notificaciones ?? true;

console.log(notif2); // false (correcto!)



const tema = config.tema ?? 'claro';

console.log(tema); // "claro" (tema era null)
Valor || (OR) ?? (Nullish)
null Usa default Usa default
undefined Usa default Usa default
0 Usa default ? Devuelve 0 ?
"" (string vacío) Usa default ? Devuelve "" ?
false Usa default ? Devuelve false ?

7. Métodos Útiles de Object

Object.keys()

Devuelve un array con las claves (propiedades) del objeto:

const persona = {

  nombre: 'Ana',

  edad: 25,

  ciudad: 'Sevilla'

};



const claves = Object.keys(persona);

console.log(claves); // ["nombre", "edad", "ciudad"]



// Útil para iterar

claves.forEach(key => {

  console.log(`${key}: ${persona[key]}`);

});

// nombre: Ana

// edad: 25

// ciudad: Sevilla

Object.values()

Devuelve un array con los valores:

const valores = Object.values(persona);

console.log(valores); // ["Ana", 25, "Sevilla"]

Object.entries()

Devuelve un array de arrays [clave, valor]:

const entradas = Object.entries(persona);

console.log(entradas);

// [["nombre", "Ana"], ["edad", 25], ["ciudad", "Sevilla"]]



// Destructuring en el bucle

for (const [clave, valor] of entradas) {

  console.log(`${clave} = ${valor}`);

}

Object.assign() - Copiar/Combinar

const target = { a: 1 };

const source = { b: 2, c: 3 };



Object.assign(target, source);

console.log(target); // { a: 1, b: 2, c: 3 }



// ? Más moderno: usar spread

const combinado = { ...target, ...source };

Object.freeze() - Inmutable

const constantes = Object.freeze({

  PI: 3.14159,

  MAX: 100

});



// ? Esto no tendrá efecto (modo estricto lanza error)

constantes.PI = 3;

console.log(constantes.PI); // 3.14159 (no cambió)

8. Ejemplo Práctico: Gestión de Usuarios

// Datos de usuario con toda la sintaxis moderna

const crearUsuario = ({ nombre, email, edad = 18, rol = 'user' }) => {

  return {

    id: Math.random().toString(36).substr(2, 9),

    nombre,

    email,

    edad,

    rol,

    fechaCreacion: new Date(),

    activo: true

  };

};



// Crear usuarios

const user1 = crearUsuario({

  nombre: 'Carlos',

  email: 'carlos@example.com',

  edad: 25

});



const user2 = crearUsuario({

  nombre: 'Laura',

  email: 'laura@example.com'

  // edad usa default: 18

});



console.log(user1);

console.log(user2);



// Actualizar usuario sin mutar

const actualizarUsuario = (usuario, cambios) => {

  return { ...usuario, ...cambios };

};



const userActualizado = actualizarUsuario(user1, {

  edad: 26,

  activo: false

});



console.log(userActualizado);



// Extraer solo los datos necesarios

const { id, nombre, email } = userActualizado;

console.log({ id, nombre, email });



// Obtener usuarios activos de una lista

const usuarios = [user1, user2, userActualizado];



const usuariosActivos = usuarios.filter(u => u.activo);

console.log('Activos:', usuariosActivos.length);

9. Ejercicios Prácticos

Ejercicio 1: Destructuring

Extrae titulo, autor y ciudad (del editor):

const libro = {

  titulo: '1984',

  autor: 'George Orwell',

  año: 1949,

  editor: {

    nombre: 'Secker & Warburg',

    ciudad: 'Londres'

  }

};



// Tu código aquí

// const { ... } = libro;

Ejercicio 2: Spread

Combina estos objetos y sobrescribe el precio:

const productoBase = { id: 1, nombre: 'Camiseta', precio: 20 };

const oferta = { precio: 15, descuento: '25%' };



// Resultado esperado: { id: 1, nombre: 'Camiseta', precio: 15, descuento: '25%' }

Ejercicio 3: Optional Chaining

Obtén el código postal SIN causar errores:

const cliente = {

  nombre: 'Pedro',

  direccion: {

    calle: 'Gran Vía'

    // codigoPostal no existe

  }

};



// const cp = ...?

console.log(cp); // undefined (sin error)
Volver a JS Volver a JS