¿Qué es un objeto en JavaScript? Explicado con ejemplos
Un objeto en JavaScript es un conjunto de pares clave/valor: una forma de agrupar bajo un mismo nombre datos que van juntos, como el nombre, la edad y los lenguajes de una persona. En otros lenguajes lo llamarás diccionario, hash o array asociativo.
Piensa en una cajonera con etiquetas: cada cajón tiene un nombre (la clave) y dentro guarda algo (el valor). Puedes tener tantos cajones como quieras, abrir cualquiera por su nombre, añadir uno nuevo o quitarlo.
Los objetos en JavaScript están por todas partes: un usuario que llega de una API, la configuración de una librería o el propio console con el que escribes en la terminal. Si entiendes cómo funcionan, entiendes buena parte del lenguaje.
Cómo se crea un objeto
La forma más directa es escribirlo entre llaves. Esto se llama literal de objeto:
const persona = {
nombre: "Ada",
edad: 36,
lenguajes: ["JavaScript", "Python"],
direccion: { ciudad: "Madrid" },
saludar() {
return `Hola, soy ${this.nombre}`;
},
};
Cada par clave: valor es una propiedad. Los valores pueden ser de cualquier tipo: textos, números, arrays, otros objetos o funciones. Una propiedad que guarda una función, como saludar, se llama método; lo vemos a fondo más abajo.
Primitivos y objetos: los dos grandes tipos de datos
Para entender qué es un objeto ayuda saber qué no lo es. JavaScript tiene siete tipos primitivos:
| Primitivo | Ejemplo | typeof |
|---|---|---|
| String (texto) | "Ada" |
"string" |
| Number (número) | 36 |
"number" |
| BigInt (entero muy grande) | 10n |
"bigint" |
| Boolean | true |
"boolean" |
| Undefined | undefined |
"undefined" |
| Symbol | Symbol("id") |
"symbol" |
| Null | null |
"object" (un error histórico) |
Todo lo demás es un objeto. La diferencia práctica es que un primitivo es un valor fijo, como un número escrito en un papel, mientras que un objeto es un contenedor que puedes ir cambiando, como la cajonera.
const texto = "Ada";
console.log(texto.toUpperCase(), texto.length); // ADA 3
try {
texto.apellido = "Lovelace";
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
// TypeError: Cannot create property 'apellido' on string 'Ada'
Un texto parece tener métodos (toUpperCase) y propiedades (length), pero no es un objeto: cuando le pides una propiedad, JavaScript lo envuelve un instante en un objeto String, lo usa y lo descarta. Por eso no puedes guardarle una propiedad nueva. En un módulo (un fichero que se carga con import y export) o en modo estricto (una versión más exigente del lenguaje, que se activa con "use strict" y que los módulos usan siempre) lanza un error; en un script clásico sin modo estricto, la propiedad se pierde en silencio y leerla da undefined.
Tipos de objetos en JavaScript
Además de los objetos que escribes entre llaves, el lenguaje trae muchos ya hechos. Todos son objetos, aunque cada uno tenga su especialidad:
| Objeto | Para qué sirve | typeof |
|---|---|---|
{} |
Agrupar datos con nombre | "object" |
Array |
Listas ordenadas | "object" |
Date |
Fechas y horas | "object" |
Map y Set |
Colecciones de claves o de valores únicos | "object" |
Function |
Código que se puede llamar | "function" |
RegExp |
Expresiones regulares | "object" |
Error |
Errores que se lanzan con throw |
"object" |
console.log(typeof {}, typeof [], typeof new Date(), typeof new Map());
// object object object object
console.log(typeof function () {}); // function
console.log([] instanceof Object, new Date() instanceof Object, (() => {}) instanceof Object);
// true true true
typeof dice "function" para las funciones, pero instanceof Object confirma que también son objetos: objetos que, además, se pueden llamar. Para los arrays, typeof no ayuda; usa Array.isArray(). Tienes sus métodos en Métodos de arrays en JavaScript.
Cómo leer una propiedad: punto o corchetes
console.log(persona.nombre); // Ada
console.log(persona["edad"]); // 36
console.log(persona.direccion.ciudad); // Madrid
console.log(persona.saludar()); // Hola, soy Ada
Las dos formas leen lo mismo. ¿Cuándo usar cada una?
| Forma | Cuándo |
|---|---|
persona.nombre |
Casi siempre: conoces el nombre de la clave al escribir el código |
persona["nombre"] |
Cuando la clave está en una variable o tiene espacios o guiones |
const campo = "edad";
console.log(persona[campo]); // 36
console.log(persona.telefono); // undefined
console.log(persona.direccion?.codigoPostal); // undefined
Si la clave no existe, obtienes undefined, no un error. El operador ?. (encadenamiento opcional) evita el error cuando lo que puede faltar es un objeto intermedio: si direccion no existiera, devolvería undefined en lugar de romper.
Añadir, cambiar y borrar propiedades
const coche = { marca: "Seat" };
coche.color = "rojo"; // añade
coche.marca = "Cupra"; // cambia
delete coche.color; // borra
console.log(coche); // { marca: 'Cupra' }
console.log("marca" in coche); // true
console.log(Object.hasOwn(coche, "color")); // false
Para saber si una propiedad existe, Object.hasOwn(objeto, clave) es la forma recomendada hoy, por encima del antiguo objeto.hasOwnProperty(clave).
¿Qué puede ser una clave?
Las claves son textos (strings) o símbolos (Symbol). Si usas otra cosa, como un número, JavaScript la convierte en texto:
const raro = { "nombre completo": "Ada Lovelace", 1: "uno" };
console.log(raro["nombre completo"]); // Ada Lovelace
console.log(Object.keys(raro)); // [ '1', 'nombre completo' ]
Fíjate en que la clave 1 sale como '1', entre comillas: es un texto.
Métodos: funciones dentro de un objeto (y this)
Un método es una función guardada en una propiedad. Sirve para que el objeto no solo guarde datos, sino que también sepa hacer cosas con ellos. Dentro del método, this es el objeto que lo llama:
const contador = {
valor: 0,
sumar() {
this.valor += 1;
return this.valor;
},
};
console.log(contador.sumar()); // 1
console.log(contador.sumar()); // 2
La regla que conviene recordar: this lo decide quien llama, no dónde se escribió el método. En contador.sumar(), lo que hay a la izquierda del punto es contador, y eso es this. Es como la palabra «yo»: no significa siempre la misma persona, sino quien la dice.
Por eso, si sacas el método del objeto y lo llamas suelto, pierde su this:
const persona = {
nombre: "Ada",
saludar() {
return `Hola, soy ${this.nombre}`;
},
};
const otra = { nombre: "Grace", saludar: persona.saludar };
console.log(otra.saludar()); // Hola, soy Grace
const suelto = persona.saludar;
try {
suelto();
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
// TypeError: Cannot read properties of undefined (reading 'nombre')
const atado = persona.saludar.bind(persona);
console.log(atado()); // Hola, soy Ada
El mismo método dice «Grace» cuando lo llama otra. Llamado suelto, en un módulo (o en modo estricto) this es undefined y falla; en un script clásico sin modo estricto, this sería el objeto global y obtendrías "Hola, soy undefined". Pasa mucho al entregar un método como callback, por ejemplo a setTimeout o a un evento. bind crea una copia de la función con el this fijado.
Las funciones flecha no tienen this propio
Una función flecha (=>) no tiene this propio: usa el del sitio donde se escribió. Como método es mala idea (en arrow functions en JavaScript están los demás casos), pero dentro de un método es justo lo que quieres:
const equipo = {
nombre: "Backend",
miembros: ["Ada", "Grace"],
presentar() {
return this.miembros.map((miembro) => `${miembro} (${this.nombre})`);
},
};
console.log(equipo.presentar()); // [ 'Ada (Backend)', 'Grace (Backend)' ]
La flecha que recibe map usa el this de presentar, que es equipo. Con una función normal en su lugar, this volvería a perderse. Hay una explicación más larga en This en JavaScript, Carlos Blé.
Recorrer un objeto: Object.keys, values y entries
const persona = { nombre: "Ada", edad: 36, ciudad: "Madrid" };
console.log(Object.keys(persona));
// [ 'nombre', 'edad', 'ciudad' ]
console.log(Object.values(persona));
// [ 'Ada', 36, 'Madrid' ]
console.log(Object.entries(persona));
// [ [ 'nombre', 'Ada' ], [ 'edad', 36 ], [ 'ciudad', 'Madrid' ] ]
for (const [clave, valor] of Object.entries(persona)) {
console.log(`${clave}: ${valor}`);
}
// nombre: Ada
// edad: 36
// ciudad: Madrid
Object.entries es la más útil para recorrerlo: te da cada clave con su valor.
Desestructuración y spread: sacar y combinar propiedades
La desestructuración saca propiedades a variables en una sola línea. Puedes renombrarlas y darles un valor por defecto:
const { nombre, edad: años, pais = "España" } = persona;
console.log(nombre, años, pais); // Ada 36 España
El operador spread (...) vuelca las propiedades de un objeto dentro de otro nuevo. Es la forma habitual de crear una versión modificada sin tocar el original:
const actualizada = { ...persona, edad: 37 };
console.log(actualizada); // { nombre: 'Ada', edad: 37, ciudad: 'Madrid' }
console.log(persona.edad); // 36
Y un atajo: si la variable se llama igual que la clave, no hace falta repetirla.
const nombre2 = "Grace";
console.log({ nombre2 }); // { nombre2: 'Grace' }
Tienes más ejemplos en Asignación por destructuring en JavaScript.
Lo más importante: la variable guarda una referencia
Con los primitivos, al asignar una variable a otra se copia el valor. Quedan independientes:
let numero1 = 3.14159;
let numero2 = numero1;
numero2 = 42;
console.log(numero1, numero2); // 3.14159 42
Con los objetos no ocurre lo mismo. La variable no guarda el objeto, sino una referencia a él: algo así como la dirección de una casa. Si copias la dirección en otro papel, sigue habiendo una sola casa. Si alguien pinta la puerta, lo verás vayas con el papel que vayas.
const objeto1 = { a: 3.14159 };
const objeto2 = objeto1; // copia la dirección, no la casa
objeto2.a = 42;
console.log(objeto1.a, objeto2.a); // 42 42
console.log(objeto1 === objeto2); // true
objeto2.a, también cambia objeto1.a.Copiar la variable no copia el objeto.
Por la misma razón, dos objetos con el mismo contenido no son iguales para ===: son dos casas distintas, aunque estén amuebladas igual.
console.log({ a: 1 } === { a: 1 }); // false
Está explicado con más detalle en Diferencias entre == y === en JavaScript.
¿Por valor o por referencia?
Se suele decir que los objetos «se pasan por referencia», pero no es del todo exacto. JavaScript siempre copia el valor de la variable; en un objeto, ese valor es la referencia. Por eso una función puede modificar el objeto que recibe, pero no sustituirlo:
function cumplirAños(p) { p.edad += 1; } // cambia la casa
function reemplazar(p) { p = { edad: 0 }; } // solo cambia su papel
const ada = { edad: 36 };
cumplirAños(ada);
console.log(ada.edad); // 37
reemplazar(ada);
console.log(ada.edad); // 37
Más ejemplos en Paso por referencia y paso por valor en JavaScript.
¿Y si necesitas una copia?
Si quieres una casa nueva y no otra copia de la dirección, tienes dos opciones:
{ ...obj }hace una copia superficial: copia el primer nivel, pero los objetos de dentro siguen compartidos.structuredClone(obj)hace una copia profunda: copia todos los niveles. No admite funciones y no conserva la clase.
const original = { nombre: "Ada", direccion: { ciudad: "Madrid" } };
const superficial = { ...original };
superficial.direccion.ciudad = "Sevilla";
console.log(original.direccion.ciudad); // Sevilla
const profunda = structuredClone(original);
profunda.direccion.ciudad = "Bilbao";
console.log(original.direccion.ciudad); // Sevilla
Con spread, cambiar la ciudad de la copia cambia la del original, porque direccion era la misma casa. Con structuredClone, no. Las diferencias, los límites de cada forma y el truco antiguo de JSON.parse(JSON.stringify(obj)) están en cómo clonar un objeto en JavaScript.
Congelar un objeto: Object.freeze
Si const no impide cambiar las propiedades, ¿cómo se protege un objeto? Con Object.freeze:
"use strict";
const config = Object.freeze({ idioma: "es", tema: { color: "oscuro" } });
try {
config.idioma = "en";
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
// TypeError: Cannot assign to read only property 'idioma' of object '#<Object>'
config.tema.color = "claro";
console.log(config); // { idioma: 'es', tema: { color: 'claro' } }
console.log(Object.isFrozen(config), Object.isFrozen(config.tema)); // true false
Dos detalles. En modo estricto (y en los módulos, que siempre lo son), cambiar un objeto congelado lanza un error; sin modo estricto, el cambio se ignora en silencio. Y freeze es superficial: congela el primer nivel, no los objetos de dentro. Es como cerrar con llave la cajonera, pero no las cajas que hay dentro de cada cajón.
Objetos y JSON
JSON es el formato de texto con el que viajan los datos entre un servidor y una web, y se parece mucho a un literal de objeto. JSON.stringify convierte un objeto en texto y JSON.parse hace el camino contrario:
const persona = {
nombre: "Ada",
edad: 36,
alta: new Date("2026-11-08T10:00:00Z"),
apodo: undefined,
saludar() { return "Hola"; },
};
const texto = JSON.stringify(persona);
console.log(texto);
// {"nombre":"Ada","edad":36,"alta":"2026-11-08T10:00:00.000Z"}
const deVuelta = JSON.parse(texto);
console.log(typeof deVuelta.alta, deVuelta.saludar); // string undefined
Fíjate en lo que se pierde por el camino: el método y la propiedad con undefined desaparecen, y la fecha vuelve como texto. JSON guarda datos, no comportamiento.
Objeto o Map: cuándo usar cada uno
Map también guarda pares clave/valor, así que es normal dudar. La documentación de MDN los compara en detalle; lo esencial:
Objeto {} |
Map |
|
|---|---|---|
| Tipos de clave | Textos y símbolos | Cualquier valor, también objetos |
| Tamaño | Object.keys(o).length |
m.size |
| Claves heredadas | Sí, del prototipo (toString…) |
Solo las que añades |
| Pasar a JSON | Directo | Hay que convertirlo |
| Muchas altas y bajas | No está optimizado | Mejor rendimiento |
const visitas = new Map();
const usuario = { nombre: "Ada" };
visitas.set(usuario, 3);
console.log(visitas.get(usuario), visitas.size); // 3 1
const comoObjeto = {};
comoObjeto[usuario] = 3;
console.log(Object.keys(comoObjeto)); // [ '[object Object]' ]
Con un objeto como clave, el objeto normal la convierte en el texto '[object Object]', y cualquier otro objeto acabaría en el mismo cajón. Como regla práctica: un objeto para describir una cosa con campos conocidos (una persona, una configuración); un Map para una colección que crece y mengua, o cuando las claves no son textos.
Clases: una forma de fabricar objetos
Si necesitas muchos objetos con la misma forma y los mismos métodos, una clase hace de molde:
class Persona {
constructor(nombre) {
this.nombre = nombre;
}
saludar() {
return `Hola, soy ${this.nombre}`;
}
}
const ada = new Persona("Ada");
console.log(ada.saludar()); // Hola, soy Ada
console.log(typeof Persona); // function
console.log(Object.keys(ada)); // [ 'nombre' ]
Las clases de JavaScript se construyen sobre prototipos: cada objeto tiene un «objeto padre» al que pregunta cuando no encuentra una propiedad. saludar no se copia en cada persona, sino que vive en Persona.prototype y todas lo comparten:
console.log(Object.getPrototypeOf(ada) === Persona.prototype); // true
console.log(Object.hasOwn(ada, "saludar")); // false
Por eso a menudo se dice que las clases son «azúcar sintáctico» sobre los prototipos. Es una buena primera idea, aunque no del todo exacta: las clases añaden reglas propias, como no poder llamarlas sin new o tener campos privados con #. Si quieres ir más allá, tienes la sintaxis completa (métodos, static, campos privados y herencia) en clases en JavaScript y cómo diseñarlas bien en Clases en código limpio.
Errores típicos con objetos
const persona = { nombre: "Ada" };
try {
console.log(persona.direccion.ciudad);
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
// TypeError: Cannot read properties of undefined (reading 'ciudad')
console.log(persona.direccion?.ciudad); // undefined
const clave = "nombre";
console.log(persona.clave, persona[clave]); // undefined Ada
const ajustes = { volumen: 0 };
console.log(ajustes.volumen || 50, ajustes.volumen ?? 50); // 50 0
console.log(`${persona}`); // [object Object]
- Leer dentro de algo que no existe.
persona.direccion.ciudadfalla si faltadireccion. Usa?.cuando un dato puede no venir. - Usar el punto con una variable.
persona.clavebusca una propiedad llamada literalmenteclave. Con una variable, corchetes:persona[clave]. - Dar un valor por defecto con
||. Trata0,""yfalsecomo si faltaran: el volumen 0 se convierte en 50.??solo actúa connullyundefined. - Meter un objeto en un texto. Sale
[object Object]. Para verlo,console.log(persona)oJSON.stringify(persona). - Creer que
const b = acopia el objeto, o que===compara el contenido. Las dos cosas trabajan con la referencia. - Sacar un método y perder
this. Al pasarlo como callback, envuélvelo en una flecha (() => persona.saludar()) o usabind.
Tu chuleta
| Quiero… | Código |
|---|---|
| Crear un objeto | const o = { clave: valor } |
| Leer una propiedad | o.clave o o["clave"] |
| Leer sin romper si falta algo | o.a?.b |
| Añadir o cambiar | o.clave = valor |
| Borrar | delete o.clave |
| Saber si existe | Object.hasOwn(o, "clave") |
| Recorrerlo | for (const [k, v] of Object.entries(o)) |
| Sacar propiedades a variables | const { clave } = o |
| Copia superficial | { ...o } |
| Copia profunda | structuredClone(o) |
| Que no se pueda cambiar | Object.freeze(o) |
| Pasarlo a texto JSON | JSON.stringify(o) |
Fuentes
Comprobado el 8 de noviembre de 2026 en MDN Web Docs:
- Trabajar con objetos: claves como strings o símbolos, métodos y objetos como tipo por referencia.
- Tipos y estructuras de datos de JavaScript: los siete primitivos, que las funciones son objetos y el envoltorio automático de los primitivos.
- this:
thisen métodos, en funciones sueltas con y sin modo estricto, en funciones flecha y conbind. - Map: la comparación entre objetos y
Map. - Object.freeze(): congelado superficial y error en modo estricto.
- JSON.stringify(), Clases, structuredClone() y Object.hasOwn().
Todos los ejemplos se han ejecutado con Node.js 24.21.0 en Linux, como módulos ES, y las salidas mostradas son las reales.