Objetos literales en JavaScript: qué son y cómo cambió su sintaxis

En esta página
Enterate del próximo post
Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.

Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.
La primera vez que escribí un objeto en JavaScript sentí que estaba haciendo trampa.
Venía de cursar Programación con Java, donde para tener un objeto hay que declarar una clase, sus atributos, su constructor, sus getters y sus setters. Recién ahí, después de todo ese ritual, aparece el new. En JavaScript abrí dos llaves y ya tenía un objeto:
const persona = {
nombre: "Fernando",
saludar() {
return `Hola, soy ${this.nombre}`;
},
};Tardé bastante en entender que no era trampa. Era otro modelo, con sus propias reglas, y confundirlo con el de Java me costó varios bugs.
En la cursada definimos un objeto por tres cosas: identidad, estado y comportamiento. El literal de arriba tiene las tres. Tiene una referencia propia, tiene datos y tiene una función que opera sobre esos datos. No le falta nada para ser un objeto en el sentido de la teoría, aunque no exista ninguna clase detrás.
Y encima es más flexible de lo que uno espera, porque la forma se define en tiempo de ejecución:
persona.edad = 24; // agrego una propiedad que no existía
delete persona.edad; // y la saco
persona["nombre"]; // acceso con corchetes: la clave es un stringEsa flexibilidad es la parte linda y la parte peligrosa. Linda porque modelar algo lleva diez segundos. Peligrosa porque nadie te avisa cuando escribís persona.nombbre y obtenés undefined en vez de un error. Es exactamente el motivo por el que después terminé usando TypeScript en este mismo portfolio.
Este objeto que parece suelto en realidad ya está heredando de algo: por eso persona.toString() funciona sin que nadie lo haya escrito. De dónde sale eso es tema aparte, con el espacio que se merece: el próximo post de la serie es exactamente sobre esa cadena de la que un objeto literal ya forma parte apenas se crea.
Lo que sí cambió muchísimo es lo que se puede escribir adentro de las llaves. Vale la pena ver el recorrido, porque mucho código que uno encuentra en tutoriales viejos hace en cinco líneas cosas que hoy se resuelven en una.
Antes de ES5 el objeto era básicamente una bolsa de pares clave-valor. ES5 trajo la maquinaria para controlarlo: Object.keys, Object.create, Object.freeze y los descriptores de propiedad con Object.defineProperty. También permitió declarar getters y setters directamente en el literal, algo que hoy usamos sin pensarlo:
const cuenta = {
_saldo: 0,
get saldo() {
return `$${this._saldo}`;
},
set saldo(valor) {
this._saldo = Math.max(0, valor);
},
};Ese _saldo con guion bajo era la única forma de decir "esto es privado, no lo toques". Una convención, no una regla. Guardá el dato, porque vuelve más abajo.
Es la versión que más cambió la forma de escribir objetos. Tres cosas concretas:
saludar: function () {}.const nombre = "Fernando";
const campo = "rol";
// Antes
const antes = {
nombre: nombre,
saludar: function () {
return "Hola";
},
};
// Después
const despues = {
nombre,
[campo]: "Full Stack",
saludar() {
return "Hola";
},
};En la misma versión llegaron Object.assign para combinar objetos y los Symbol como claves únicas que no chocan con nada. También llegó la sintaxis class, que en el fondo sigue siendo un objeto con un prototipo, así que la dejo para el post que le corresponde en vez de meterla acá de pasada.
Acá el objeto se acercó al array. Object.values y Object.entries (ES2017) permitieron recorrerlo con las herramientas que ya usábamos para listas, y Object.fromEntries (ES2019) cerró el círculo para volver:
const precios = { teclado: 25000, mouse: 12000 };
const conIva = Object.fromEntries(
Object.entries(precios).map(([producto, precio]) => [
producto,
precio * 1.21,
]),
);
// { teclado: 30250, mouse: 14520 }En el medio, ES2018 sumó el spread y el rest de objetos, que es probablemente la sintaxis que más uso hoy:
const porDefecto = { tema: "oscuro", idioma: "es" };
const config = { ...porDefecto, idioma: "en" }; // el último gana
const { idioma, ...resto } = config; // rest: saco una claveOjo con esto: el spread hace una copia superficial. Si el objeto tiene otro objeto adentro, los dos van a compartir esa referencia. Cuando necesito una copia real uso structuredClone.
Trabajar con respuestas de una API era una cadena de if para no explotar en un nivel intermedio. El encadenamiento opcional y el operador de coalescencia nula lo resolvieron:
const ciudad = usuario?.direccion?.ciudad ?? "Sin definir";Y en ES2022 llegó Object.hasOwn, el reemplazo prolijo de hasOwnProperty: no se rompe cuando el objeto no tiene prototipo, ni cuando una clave de afuera se llama justo hasOwnProperty.
const datos = JSON.parse('{"hasOwnProperty": "sorpresa"}');
datos.hasOwnProperty("id"); // TypeError: no es una función
Object.hasOwn(datos, "id"); // false, y sigue de largoES2024 sumó Object.groupBy, que agrupa una colección por criterio y devuelve un objeto, algo que antes se hacía a mano con reduce. Y de ahí para acá las ediciones nuevas se fueron para otro lado: helpers de iteradores, Temporal para fechas, manejo explícito de recursos con using. Lo leo como una buena señal: al objeto literal ya no le falta nada urgente.
Después de dar vueltas con esto, lo que me ordenó la cabeza fue dejar de traducir Java a JavaScript. En la práctica uso literales para casi todo: configuraciones, respuestas de API, datos que solo necesitan viajar de un lado a otro.
El objeto literal no es una versión recortada de una clase. Es el tipo de dato con el que arrancó el lenguaje, y todo lo que se agregó después se construyó alrededor de esa misma bolsa de pares clave-valor.
Si estás empezando, el objeto literal alcanza para casi todo lo que vas a necesitar al principio: no hace falta una clase para tener algo con estado y comportamiento propio.
Lo que quedó pendiente acá es de dónde sale el resto de lo que un objeto puede hacer sin que nadie lo haya escrito a mano, como ese persona.toString() del principio. Ese es el próximo tema de la serie: la cadena de prototipos.