Objetos en JavaScript: propiedades, spread y destructuring

Actualizado el 29 de septiembre de 2026

Un objeto agrupa datos relacionados bajo nombres: un producto con su nombre, precio y stock; un usuario con su correo y su ciudad. Casi todos los datos con los que trabaja una aplicación web (lo que llega de una API, lo que se guarda en una base de datos) son objetos o arrays de objetos.

En esta guía vas a ver cómo leer y modificar objetos, recorrerlos, copiarlos sin modificar el original y la desestructuración (destructuring), una sintaxis que vas a encontrar en todo el código moderno.

Crear un objeto y leer sus propiedades

const producto = { nombre: "Polerón", precio: 19990, tallas: ["S", "M", "L"] };

producto.nombre       // 'Polerón'
producto["precio"]    // 19990
producto.tallas[1]    // 'M'
producto.color        // undefined (no existe, pero no es un error)

const campo = "precio";
producto[campo]       // 19990  — con corchetes se puede usar una variable

La notación de punto es la habitual. Los corchetes son necesarios cuando el nombre de la propiedad está en una variable o no es un nombre válido (por ejemplo, si tiene espacios).

Agregar, cambiar y eliminar

producto.stock = 12;       // agrega
producto.precio = 17990;   // modifica
delete producto.tallas;    // elimina
// producto → { nombre: 'Polerón', precio: 17990, stock: 12 }

"stock" in producto        // true
Object.hasOwn(producto, "color")  // false

Recorrer un objeto

Object.keys(producto)     // ['nombre', 'precio', 'stock']
Object.values(producto)   // ['Polerón', 17990, 12]

for (const [clave, valor] of Object.entries(producto)) {
  console.log(`${clave}: ${valor}`);
}
// nombre: Polerón
// precio: 17990
// stock: 12

Object.entries() convierte el objeto en un array de pares [clave, valor], que se recorre con for...of. Al convertirlo en array, también se pueden usar map, filter y el resto de los métodos de array.

Copiar y combinar con spread

const oferta = { ...producto, precio: 15990 };

producto.precio   // 17990 (el original no cambió)
oferta.precio     // 15990

{ ...objeto } copia todas las propiedades en un objeto nuevo, y las que se escriben después reemplazan a las copiadas. Es la forma estándar de "modificar" un objeto sin tocar el original, algo que frameworks como React exigen.

Destructuring

La desestructuración saca propiedades de un objeto (o elementos de un array) y las guarda en variables, en una línea:

const { nombre, precio } = producto;
// nombre → 'Polerón', precio → 17990

const { color = "negro", nombre: titulo } = producto;
// color → 'negro' (valor por defecto, porque no existe)
// titulo → 'Polerón' (renombrado)

const [primero, , tercero] = ["a", "b", "c"];
// primero → 'a', tercero → 'c'

Donde más se usa es en los parámetros de una función: la función declara qué propiedades necesita, y quien la llama pasa un objeto.

function mostrar({ nombre, precio }) {
  return `${nombre}: $${precio}`;
}
mostrar(producto);   // 'Polerón: $17990'

Encadenamiento opcional: ?.

const usuario = { perfil: { ciudad: "Talca" } };

usuario.perfil?.ciudad                          // 'Talca'
usuario.direccion?.calle                        // undefined, sin error
usuario.direccion?.calle ?? "sin dirección"     // 'sin dirección'

?. deja de avanzar y devuelve undefined si lo que está a su izquierda es null o undefined. Combinado con ?? sirve para leer datos que pueden faltar, algo muy común con respuestas de APIs.

Errores comunes

Leer una propiedad de undefined

usuario.direccion.calle lanza TypeError: Cannot read properties of undefined (reading 'calle') si direccion no existe. Es probablemente el error más común de JavaScript. Revisa de dónde viene el dato, o usa ?. si es normal que falte.

objeto.variable en vez de objeto[variable]

Con const campo = "precio", producto.campo busca una propiedad que se llama literalmente "campo" y devuelve undefined. Para usar el valor de la variable como nombre, escribe producto[campo].

La copia con spread es superficial

const original = { nombre: "Ana", notas: [5, 6] };
const copia = { ...original };
copia.nombre = "Luis";   // no afecta al original
copia.notas.push(7);     // ¡sí lo afecta!
// original → { nombre: 'Ana', notas: [5, 6, 7] }

Spread copia el primer nivel. Los arrays y objetos de adentro se comparten entre el original y la copia. Para copiar todo en profundidad existe structuredClone(original).

Comparar objetos con ===

{ a: 1 } === { a: 1 } es false: son dos objetos distintos aunque tengan el mismo contenido. === solo es true si ambos lados son el mismo objeto.

Ejercicios resueltos

Intenta resolver cada ejercicio antes de abrir la solución: equivocarse y corregir es la parte que más enseña.

1.Resumen de un producto

Escribe una función resumenProducto que reciba un objeto con nombre, precio y stock (opcional, 0 si falta) y devuelva un texto como "Polerón ($19990): 3 disponibles", o "agotado" en vez de la cantidad si no hay stock. Usa destructuring en el parámetro.

  • resumenProducto({ nombre: "Polerón", precio: 19990, stock: 3 }) → "Polerón ($19990): 3 disponibles"
  • resumenProducto({ nombre: "Gorro", precio: 5990 }) → "Gorro ($5990): agotado"
Punto de partida
function resumenProducto(/* ... */) {
  // ...
}
Ver solución
Solución
function resumenProducto({ nombre, precio, stock = 0 }) {
  const disponibilidad = stock > 0 ? `${stock} disponibles` : "agotado";
  return `${nombre} ($${precio}): ${disponibilidad}`;
}

El parámetro desestructurado deja a la vista qué propiedades usa la función, y stock = 0 resuelve el caso en que falta. Separar la disponibilidad en su propia variable evita meter un ternario dentro del template literal, que se vuelve difícil de leer. El $ antes de ${precio} es un signo de peso literal.

2.Contar palabras

Escribe una función contarPalabras(texto) que devuelva un objeto con cuántas veces aparece cada palabra, sin distinguir mayúsculas e ignorando espacios repetidos.

  • contarPalabras("el sol y el mar") → { el: 2, sol: 1, y: 1, mar: 1 }
  • contarPalabras("Hola hola") → { hola: 2 }
  • contarPalabras("") → {}
Punto de partida
function contarPalabras(texto) {
  // ...
}
Ver solución
Solución
function contarPalabras(texto) {
  const conteo = {};
  const palabras = texto.toLowerCase().split(" ").filter((p) => p !== "");
  for (const palabra of palabras) {
    conteo[palabra] = (conteo[palabra] ?? 0) + 1;
  }
  return conteo;
}

Cada palabra es una clave y su valor es la cantidad. conteo[palabra] ?? 0 devuelve la cantidad actual o 0 si la palabra aparece por primera vez. Aquí la palabra está en una variable, así que hay que usar corchetes.

El filter quita los textos vacíos que genera split(" ") con espacios repetidos o con un texto vacío ("".split(" ") devuelve [""], no un array vacío).

3.Actualizar sin modificar el original

Escribe una función actualizarUsuario(usuario, cambios) que devuelva un objeto nuevo con los datos del usuario y los cambios aplicados, sin modificar el objeto original.

Ejemplo
const u = { nombre: "Ana", ciudad: "Talca", edad: 30 };
const u2 = actualizarUsuario(u, { ciudad: "Santiago" });
// u2 → { nombre: 'Ana', ciudad: 'Santiago', edad: 30 }
// u  → { nombre: 'Ana', ciudad: 'Talca', edad: 30 }  (sin cambios)
Punto de partida
function actualizarUsuario(usuario, cambios) {
  // ...
}
Ver solución
Solución
function actualizarUsuario(usuario, cambios) {
  return { ...usuario, ...cambios };
}

Se copian primero las propiedades del usuario y después las de los cambios. Cuando una propiedad está en ambos, gana la que viene después, así que ciudad queda en "Santiago". Hacer usuario.ciudad = cambios.ciudad también daría el resultado, pero modificaría el objeto original, que es justo lo que se pide evitar.

Practica JavaScript con ejercicios a tu nivel

En Coditer resuelves ejercicios en el navegador, con tests automáticos que revisan tu código y una IA que te da pistas cuando te atascas. Practicar es gratis.