Command Palette

Search for a command to run...

Tipos de Referencia y Objetos en JavaScript

Entiende la diferencia fundamental entre tipos primitivos y de referencia. Aprende cómo funcionan las referencias en memoria, cómo copiar objetos correctamente y evitar bugs comunes.

Lectura: 11 min
Nivel: Principiante

TL;DR - Resumen rápido

  • Primitivos se copian por valor, objetos se copian por referencia
  • Objetos son colecciones de pares clave-valor. Arrays, funciones y fechas también son objetos
  • Dos variables pueden apuntar al mismo objeto en memoria - cambios en una afectan la otra
  • Spread (...) y Object.assign() hacen copias shallow - objetos anidados siguen siendo referencias
  • Para copias deep usa structuredClone() o JSON.parse(JSON.stringify())
  • Object.keys(), Object.values(), Object.entries() retornan arrays de las propiedades del objeto
  • Comparar objetos con === compara referencias, no contenido - {} === {} es siempre false

Primitivos vs Tipos de Referencia

JavaScript tiene dos categorías fundamentales de tipos: primitivos y de referencia. Los tipos primitivos (string, number, boolean, null, undefined, symbol, bigint) se copian por valor - cuando asignas una variable primitiva a otra, copias el valor. Los tipos de referencia (objetos, arrays, funciones) se copian por referencia - cuando asignas un objeto a otra variable, ambas apuntan al mismo objeto en memoria.

Esta diferencia es crítica y causa muchos bugs en desarrolladores que están aprendiendo. Con primitivos, cada variable tiene su propia copia independiente del valor. Con objetos, múltiples variables pueden referenciar el mismo objeto, por lo que modificar el objeto a través de una variable afecta a todas las demás que lo referencian.

primitivos-vs-referencia.js
Loading code...

El ejemplo demuestra claramente la diferencia. Con el primitivo, cambiar num2 no afecta num1 porque cada variable tiene su propia copia del valor. Con el objeto, cambiar obj2.name también cambia obj1.name porque ambas variables apuntan al mismo objeto en memoria. No hay dos objetos separados - hay un solo objeto con dos referencias a él.

¿Qué son Tipos de Referencia?

Cuando creas un objeto, JavaScript lo almacena en memoria y la variable guarda la "dirección" (referencia) a esa ubicación, no el objeto en sí. Por eso obj2 = obj1 no crea un nuevo objeto - copia la referencia, haciendo que ambas variables apunten a la misma ubicación de memoria.

¿Qué son los Objetos?

Los objetos en JavaScript son colecciones de pares clave-valor, donde cada clave (también llamada propiedad) es un string o Symbol, y el valor puede ser cualquier tipo de dato: primitivos, otros objetos, arrays, funciones. Los objetos son la estructura de datos fundamental de JavaScript - arrays, funciones, fechas, expresiones regulares, y prácticamente todo excepto primitivos son objetos bajo el capó.

Incluso los primitivos tienen "object wrappers" temporales que les permiten tener métodos: por ejemplo, "hello".toUpperCase() funciona porque JavaScript envuelve el string momentáneamente en un objeto. Esta uniformidad hace JavaScript muy flexible, aunque requiere entender objetos profundamente para evitar errores.

objeto-basico.js
Loading code...

Los objetos literales se crean con llaves {}. Las propiedades pueden contener cualquier valor: primitivos, arrays, otros objetos, o funciones (llamadas métodos cuando están dentro de un objeto). Puedes anidar objetos infinitamente, crear propiedades dinámicamente, y JavaScript es extremadamente flexible con la estructura de los objetos.

Crear Objetos: Diferentes Formas

Hay varias formas de crear objetos en JavaScript. La más común es la notación literal con llaves{}. También puedes usar el constructor Object(), Object.create(), o clases (que son azúcar sintáctica sobre funciones constructoras). Para la mayoría de casos, la notación literal es la forma más clara y directa.

crear-objetos.js
Loading code...

La notación literal {} es la forma preferida por su claridad y brevedad. Object.create() es útil cuando necesitas control sobre la cadena de prototipos. new Object() funciona pero es innecesario - {} hace lo mismo de forma más concisa. Las propiedades computadas con corchetes permiten nombres de propiedades dinámicos, y la sintaxis abreviada es útil cuando la propiedad tiene el mismo nombre que la variable.

Acceder y Modificar Propiedades

Hay dos formas de acceder a propiedades de objetos: notación de punto (objeto.propiedad) y notación de corchetes (objeto['propiedad']). La notación de punto es más común y legible, pero los corchetes son necesarios cuando el nombre de la propiedad es dinámico, contiene espacios, o empieza con número.

acceder-propiedades.js
Loading code...

La notación de punto funciona cuando conoces el nombre de la propiedad en tiempo de escritura. Los corchetes permiten nombres dinámicos - útil cuando iteras propiedades o cuando el nombre viene de una variable. Puedes agregar propiedades nuevas simplemente asignándolas, y eliminar propiedades con el operador delete. El operador in verifica si una propiedad existe en el objeto o en su cadena de prototipos.

Propiedades Inexistentes

Acceder a una propiedad que no existe retorna undefined, no un error. Esto puede causar bugs sutiles: user.address.city causará TypeError si address no existe (no se puede leer una propiedad de undefined). Usa optional chaining (?.) para protegerte: user?.address?.city retorna undefined si cualquier parte de la cadena no existe.

Referencias en Memoria: El Concepto Clave

Entender cómo funcionan las referencias es absolutamente crucial en JavaScript. Cuando asignas un objeto a una variable, la variable no contiene el objeto en sí - contiene una referencia (puntero) a la ubicación del objeto en memoria. Múltiples variables pueden tener referencias al mismo objeto, y modificar el objeto a través de cualquier referencia afecta a todas las demás.

referencias-memoria.js
Loading code...

Este comportamiento es intencional y útil - permite pasar objetos grandes a funciones sin copiar toda la data. Pero puede causar bugs si no lo entiendes. Cuando pasas un objeto a una función, la función puede modificar el objeto original. Cuando guardas un objeto en un array, el array guarda la referencia, no una copia. Este es el origen de muchos bugs difíciles de rastrear.

Mutabilidad Accidental

Un error común es pensar que const protege el contenido de objetos. const user = {} previene reasignar user, pero NO previene modificar user.name o user.age. Para objetos verdaderamente inmutables, usa Object.freeze(), pero es shallow - solo congela el primer nivel de propiedades.

Copiar Objetos: Shallow vs Deep Copy

Copiar objetos correctamente es un desafío común. Hay dos tipos de copias: shallow (superficial) y deep (profunda). Una copia shallow copia el primer nivel de propiedades, pero propiedades que son objetos siguen siendo referencias al original. Una copia deep copia recursivamente todos los niveles, creando objetos completamente independientes.

copiar-objetos.js
Loading code...

El spread operator (...) y Object.assign() son las formas más comunes de hacer copias shallow. Funcionan bien para objetos planos (sin anidación). Para copias deep, structuredClone() es la solución moderna (disponible en todos los navegadores actuales). JSON.parse(JSON.stringify()) funciona pero tiene limitaciones: no copia funciones, undefined, Symbols, o valores especiales como NaN.

Cuándo Usar Qué Tipo de Copia

Usa shallow copy cuando el objeto es plano sin anidación o sabes que no modificarás objetos anidados. Usa deep copy (structuredClone()) cuando el objeto tiene múltiples niveles de anidación y necesitas independencia total del original.

Métodos Útiles del Objeto Object

JavaScript proporciona varios métodos estáticos en el objeto Object para trabajar con objetos. Los más importantes que usarás día a día son:

  • <code>Object.keys(obj)</code> — retorna un array con los nombres de las propiedades propias del objeto
  • <code>Object.values(obj)</code> — retorna un array con los valores de las propiedades
  • <code>Object.entries(obj)</code> — retorna un array de pares <code>[clave, valor]</code>
  • <code>Object.freeze(obj)</code> — hace el objeto inmutable (shallow)
  • <code>Object.assign(target, source)</code> — copia propiedades de <code>source</code> a <code>target</code>
metodos-object.js
Loading code...

Object.keys(), values(), y entries() son esenciales para iterar objetos. Object.freeze() hace un objeto inmutable de forma shallow, útil para constantes de configuración. Object.seal() previene agregar o eliminar propiedades pero permite modificar las existentes. Object.hasOwn() (moderno) o hasOwnProperty() verifican si una propiedad pertenece al objeto directamente, no a su prototipo.

Iterar Objetos Correctamente

Para iterar objetos, usa Object.keys(obj).forEach() o for...of con Object.entries(). Evita for...in sin verificar hasOwnProperty() porque itera propiedades heredadas del prototipo. Los métodos Object.keys/values/entries solo retornan propiedades propias del objeto, no heredadas, lo que los hace más seguros.

Errores Comunes con Objetos

Los errores más comunes con objetos vienen de no entender referencias: comparar objetos con === (compara referencias, no contenido), modificar objetos sin querer (por referencias compartidas), copiar superficialmente cuando necesitas copia profunda, y asumir que const hace objetos inmutables.

errores-comunes.js
Loading code...

El error más común es comparar objetos con ===, esperando que compare contenido - solo compara si son el mismo objeto en memoria. Para comparar contenido, usa JSON.stringify() para objetos simples o bibliotecas como Lodash para casos complejos. Modificar parámetros de función afecta el original porque se pasa la referencia. const no protege propiedades del objeto, solo la reasignación de la variable.

Resumen: Tipos de Referencia y Objetos

Conceptos clave:

  • Primitivos se copian por valor, objetos por referencia
  • Múltiples variables pueden apuntar al mismo objeto en memoria
  • Comparar con === compara referencias, no contenido del objeto
  • Spread (...) hace copia shallow - objetos anidados siguen siendo referencias
  • structuredClone() crea copias deep completamente independientes

Mejores prácticas:

  • Usa notación literal {} para crear objetos en lugar de new Object()
  • Copia objetos explícitamente con spread o structuredClone() según necesidad
  • Usa Object.keys/values/entries para iterar objetos de forma segura
  • Recuerda: const previene reasignación, no mutación de propiedades
  • Evita for...in sin hasOwnProperty() para no iterar propiedades heredadas