Volver al curso

JavaScript Desde Cero: Tu Primer Lenguaje de Programación

leccion
12 / 22
beginner
8 horas
Arrays, Objetos y el DOM

Objetos en JavaScript: Propiedades, Métodos y Patrones

Lectura
40 min~8 min lectura

Objetos en JavaScript: Propiedades, Métodos y Patrones

Objetivos de aprendizaje

Al finalizar esta lección serás capaz de:

  • Crear y manipular objetos usando diferentes sintaxis y patrones
  • Acceder, agregar, modificar y eliminar propiedades de objetos
  • Utilizar métodos estáticos de Object (keys, values, entries, assign, freeze)
  • Trabajar con objetos anidados y desestructuración avanzada
  • Aplicar patrones comunes como Factory Functions y Object Composition

1. Creación y manipulación de objetos

Los objetos son colecciones de pares clave-valor. Son la estructura de datos más importante de JavaScript porque TODO en JavaScript es un objeto (o se comporta como uno).

Creación de objetos

// Literal de objeto (más común)
const persona = {
  nombre: "Ana García",
  edad: 28,
  profesion: "Desarrolladora",
  hobbies: ["programar", "leer", "correr"],
  direccion: {
    calle: "Av. Corrientes 1234",
    ciudad: "Buenos Aires",
    pais: "Argentina"
  }
};

// Propiedades computadas (nombre dinámico)
const campo = "email";
const usuario = {
  nombre: "Carlos",
  [campo]: "[email protected]",
  [`${campo}Verificado`]: true
};
console.log(usuario.email); // "[email protected]"
console.log(usuario.emailVerificado); // true

// Shorthand properties (cuando variable y propiedad se llaman igual)
const nombre = "María";
const edad = 22;
const activo = true;

const perfil = { nombre, edad, activo };
// Equivale a: { nombre: nombre, edad: edad, activo: activo }

// Shorthand methods
const calculadora = {
  sumar(a, b) { return a + b; },
  restar(a, b) { return a - b; },
  multiplicar(a, b) { return a * b; },
};

Acceder a propiedades

const producto = {
  nombre: "Laptop",
  precio: 999,
  "en oferta": true, // Propiedad con espacio
  detalles: {
    marca: "HP",
    ram: "16GB"
  }
};

// Notación de punto (preferida)
console.log(producto.nombre);        // "Laptop"
console.log(producto.detalles.marca); // "HP"

// Notación de corchetes (necesaria para nombres dinámicos o con espacios)
console.log(producto["en oferta"]);   // true

const propiedad = "precio";
console.log(producto[propiedad]);     // 999

// Optional chaining para acceso seguro
console.log(producto?.detalles?.color);      // undefined (sin error)
console.log(producto?.garantia?.meses);      // undefined (sin error)

Modificar objetos

const usuario = { nombre: "Ana", edad: 28 };

// Agregar propiedades
usuario.email = "[email protected]";
usuario["telefono"] = "+54 11 1234-5678";

// Modificar
usuario.edad = 29;

// Eliminar
delete usuario.telefono;

// Verificar si una propiedad existe
console.log("nombre" in usuario);             // true
console.log("telefono" in usuario);            // false
console.log(usuario.hasOwnProperty("email"));  // true

Métodos estáticos de Object

const config = {
  tema: "oscuro",
  idioma: "es",
  notificaciones: true,
  fontSize: 14
};

// Object.keys() — array de nombres de propiedades
console.log(Object.keys(config));
// ["tema", "idioma", "notificaciones", "fontSize"]

// Object.values() — array de valores
console.log(Object.values(config));
// ["oscuro", "es", true, 14]

// Object.entries() — array de pares [clave, valor]
console.log(Object.entries(config));
// [["tema","oscuro"], ["idioma","es"], ["notificaciones",true], ["fontSize",14]]

// Iterar con Object.entries()
for (const [clave, valor] of Object.entries(config)) {
  console.log(`${clave}: ${valor}`);
}

// Object.assign() — copiar/mezclar objetos
const defaults = { tema: "claro", idioma: "en", fontSize: 12 };
const userConfig = { tema: "oscuro", fontSize: 16 };
const finalConfig = Object.assign({}, defaults, userConfig);
console.log(finalConfig); // { tema: "oscuro", idioma: "en", fontSize: 16 }

// Spread es la alternativa moderna a Object.assign
const finalConfig2 = { ...defaults, ...userConfig };
// Mismo resultado

// Object.freeze() — hacer inmutable (no se pueden agregar, modificar ni eliminar props)
const CONSTANTES = Object.freeze({
  MAX_INTENTOS: 3,
  TIMEOUT: 5000,
  API_URL: "https://api.ejemplo.com"
});
CONSTANTES.MAX_INTENTOS = 10; // Silenciosamente falla (o error en strict mode)
console.log(CONSTANTES.MAX_INTENTOS); // 3 (no cambió)

// Object.fromEntries() — crear objeto desde array de pares
const pares = [["nombre", "Ana"], ["edad", 28], ["ciudad", "Buenos Aires"]];
const obj = Object.fromEntries(pares);
console.log(obj); // { nombre: "Ana", edad: 28, ciudad: "Buenos Aires" }

2. Desestructuración avanzada de objetos
const pedido = {
  id: "ORD-001",
  cliente: {
    nombre: "Ana García",
    email: "[email protected]",
    direccion: {
      calle: "Av. Corrientes 1234",
      ciudad: "Buenos Aires"
    }
  },
  items: [
    { producto: "Laptop", precio: 999, cantidad: 1 },
    { producto: "Mouse", precio: 29, cantidad: 2 }
  ],
  total: 1057,
  fecha: "2026-02-20"
};

// Desestructuración básica
const { id, total, fecha } = pedido;

// Desestructuración anidada
const { cliente: { nombre: nombreCliente, email } } = pedido;
console.log(nombreCliente); // "Ana García"

// Desestructuración profunda con default
const { cliente: { direccion: { codigoPostal = "N/A" } } } = pedido;
console.log(codigoPostal); // "N/A" (no existe, usa default)

// Renombrar propiedades
const { id: orderId, total: montoTotal } = pedido;
console.log(orderId);    // "ORD-001"
console.log(montoTotal); // 1057

// Rest en objetos (recoger el resto)
const { id: _, cliente: __, ...restoPedido } = pedido;
console.log(restoPedido); // { items: [...], total: 1057, fecha: "2026-02-20" }

// En parámetros de funciones
function procesarPedido({ id, cliente: { nombre }, total, items }) {
  console.log(`Pedido ${id} de ${nombre}`);
  console.log(`Total: $${total} (${items.length} items)`);
}
procesarPedido(pedido);

3. Patrones con objetos

Factory Functions

function crearUsuario(nombre, email, plan = "free") {
  let _loginCount = 0; // privado vía closure
  
  return {
    nombre,
    email,
    plan,
    get loginCount() { return _loginCount; },
    login() {
      _loginCount++;
      console.log(`${nombre} inició sesión (${_loginCount} veces)`);
    },
    upgrade(nuevoPlan) {
      const anterior = plan;
      plan = nuevoPlan;
      console.log(`${nombre}: ${anterior} → ${nuevoPlan}`);
    },
    toString() {
      return `${nombre} (${email}) - Plan: ${plan}`;
    }
  };
}

const ana = crearUsuario("Ana", "[email protected]", "premium");
ana.login(); // "Ana inició sesión (1 veces)"
ana.login(); // "Ana inició sesión (2 veces)"
console.log(ana.loginCount); // 2
console.log(String(ana)); // "Ana ([email protected]) - Plan: premium"

Mapas (Map) vs Objetos

// Cuando usar Map en vez de objeto:
// 1. Claves que no son strings
// 2. Necesitás saber el tamaño fácilmente
// 3. Iteración frecuente en orden de inserción
// 4. Agregar/quitar propiedades frecuentemente

const cache = new Map();
cache.set("usuario:1", { nombre: "Ana" });
cache.set("usuario:2", { nombre: "Carlos" });
cache.set(42, "respuesta"); // Clave numérica

console.log(cache.get("usuario:1")); // { nombre: "Ana" }
console.log(cache.has("usuario:3")); // false
console.log(cache.size);             // 3

cache.delete("usuario:2");

for (const [clave, valor] of cache) {
  console.log(`${clave}:`, valor);
}

Comparar y clonar objetos

// Copia superficial (shallow copy)
const original = { nombre: "Ana", hobbies: ["leer", "correr"] };
const copiaSpread = { ...original };
const copiaAssign = Object.assign({}, original);

copiaSpread.nombre = "Carlos"; // No afecta al original
copiaSpread.hobbies.push("nadar"); // ¡SÍ afecta al original! (referencia compartida)

console.log(original.hobbies); // ["leer", "correr", "nadar"] ← problema

// Copia profunda (deep copy)
const copiaDeep = structuredClone(original); // ES2022+
copiaDeep.hobbies.push("programar");
console.log(original.hobbies); // No cambia ← correcto

// Comparar objetos (por contenido)
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
console.log(obj1 === obj2);                              // false (referencia)
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true (contenido)

Errores comunes de principiantes
  1. Confundir copia superficial con profunda: Spread {...obj} solo copia el primer nivel. Objetos y arrays internos se comparten por referencia.

  2. Usar delete en vez de spread para quitar propiedades: delete muta el original. Preferí: const { propAQuitar, ...resto } = obj.

  3. Olvidar que los objetos se pasan por referencia: Si pasás un objeto a una función y lo modificás, el original cambia.

  4. No usar optional chaining: obj.a.b.c puede dar TypeError si a o b son undefined. Usá obj?.a?.b?.c.

  5. Comparar objetos con ===: Esto compara referencias, no contenido. Dos objetos con las mismas propiedades no son "iguales" con ===.


Puntos clave de esta lección
  1. Los objetos son pares clave-valor y la estructura de datos más fundamental de JavaScript.
  2. Usá notación de punto para acceso estático y corchetes para acceso dinámico.
  3. Object.keys/values/entries son esenciales para iterar sobre objetos.
  4. Spread {...obj} crea copias superficiales; structuredClone() crea copias profundas.
  5. Object.freeze() hace un objeto inmutable (útil para constantes de configuración).
  6. Las Factory Functions combinan closures con objetos para encapsular datos y comportamiento.
  7. Usá Map en vez de objetos cuando necesitás claves no-string, iteración ordenada o tamaño dinámico.

Quiz de autoevaluación

1. ¿Cómo se accede a una propiedad con nombre dinámico?
a) obj.variable
b) obj[variable]
c) obj->variable
d) obj(variable)

2. ¿Qué retorna Object.entries({a: 1, b: 2})?
a) ["a", "b"]
b) [1, 2]
c) [["a", 1], ["b", 2]]
d) {a: 1, b: 2}

3. ¿Qué hace Object.freeze()?
a) Congela la ejecución de JavaScript
b) Hace el objeto inmutable
c) Elimina el objeto
d) Copia el objeto

4. ¿Qué es una shallow copy?
a) Una copia que incluye todos los niveles de anidación
b) Una copia solo del primer nivel (referencias internas se comparten)
c) Una copia que elimina propiedades
d) No existe ese concepto

5. ¿{ a: 1 } === { a: 1 } es true o false?
a) true
b) false
c) Error
d) Depende del navegador

Respuestas: 1-b, 2-c, 3-b, 4-b, 5-b


💡 Concepto Clave

Revisemos los puntos más importantes de esta lección antes de continuar.

Ejercicio práctico

Misión: Mini base de datos en memoria

Creá una función crearBaseDatos(nombre) que retorne un objeto con CRUD completo:

  1. insertar(registro) — agrega un registro con id autoincremental
  2. buscarPorId(id) — retorna el registro o null
  3. buscar(criterio) — busca registros que cumplan un criterio parcial
  4. actualizar(id, cambios) — actualiza un registro existente (merge)
  5. eliminar(id) — elimina un registro
  6. listar() — retorna todos los registros
  7. estadisticas() — retorna { total, primero, ultimo }

Los datos internos deben ser privados (closure).

const db = crearBaseDatos("usuarios");
db.insertar({ nombre: "Ana", edad: 28 });       // { id: 1, nombre: "Ana", edad: 28 }
db.insertar({ nombre: "Carlos", edad: 35 });     // { id: 2, nombre: "Carlos", edad: 35 }
db.actualizar(1, { edad: 29, email: "[email protected]" });
console.log(db.buscar({ nombre: "Ana" }));        // [{ id: 1, nombre: "Ana", edad: 29, email: "[email protected]" }]
console.log(db.estadisticas());                   // { total: 2, primero: {...}, ultimo: {...} }
🧠 Pon a prueba tu conocimiento
¿Cuál es el aspecto más importante que aprendiste en esta lección?
  • Comprendo el concepto principal y puedo explicarlo con mis palabras
  • Entiendo cómo aplicarlo en mi situación específica
  • Necesito repasar algunas partes antes de continuar
  • Quiero ver más ejemplos prácticos del tema
✅ ¡Excelente! Continúa con la siguiente lección para profundizar más.