JAVASCRIPT - OBJETOS CON CLAVE DINÁMICA

CUANDO LA PROPIEDAD DEL OBJETO VIENE EN UNA VARIABLE

JAVASCRIPT - OBJETOS CON CLAVE DINÁMICA

Cuando no sabes cómo se hace, parece complicado, y cuando lo ves hecho bien, te preguntas por qué no se te había ocurrido. Es un truco tan simple que hasta es fácil que se te olvide si no lo usas a menudo. En mi caso lo usé un montón cuando trabajaba en el front, sobre todo con formularios.

El problema es este: tienes una variable con un nombre, y quieres que sea la clave de un objeto. Algo tipo:

const fieldName = 'email';
const obj = { fieldName: 'usuario@example.com' };

console.log(obj); // { fieldName: 'usuario@example.com' }
console.log(obj[fieldName]); // undefined

¿Ves? Si lo haces así, lo que te queda es un objeto con la clave literal 'fieldName', no con la clave 'email' como querías. Y luego cuando accedes con obj[fieldName], no encuentra nada. Es un error tonto, y lo traicionero es que el código no falla: simplemente no hace lo que esperabas.

La solución: corchetes en la definición

La respuesta es rodear el nombre de la variable con corchetes directamente en la definición del objeto. Eso se llama computed property names:

const fieldName = 'email';
const obj = { [fieldName]: 'usuario@example.com' };

console.log(obj); // { email: 'usuario@example.com' }
console.log(obj[fieldName]); // usuario@example.com
console.log(obj.email); // usuario@example.com

Ahora sí. Los corchetes le dicen a JavaScript "toma el valor de la variable, no la cadena literal".

Funciona con expresiones también

No tiene por qué ser solo una variable. Puede ser cualquier expresión que devuelva un string (o algo convertible a string, como un número):

const prefix = 'user_';
const id = 123;

const obj = {
    [prefix + id]: 'Jaime',
    [`generated_${id}`]: true
};

console.log(obj.user_123); // Jaime
console.log(obj['generated_123']); // true

También vale un Symbol como clave, pero ojo: esas claves no salen en Object.keys() ni en JSON.stringify(), así que no las uses para datos que quieras recorrer o serializar.

Un caso de uso real: formularios

Donde más lo usé fue en formularios. Tienes un único manejador para todos los campos, y el nombre del campo que ha cambiado te llega en una variable. Un ejemplo con React:

const [form, setForm] = useState({ name: '', email: '' });

const handleChange = (e) => {
    // e.target.name vale 'name' o 'email', según el input que haya cambiado
    setForm({ ...form, [e.target.name]: e.target.value });
};

Sin los corchetes tendrías que escribir un manejador por campo, o un switch con un caso para cada uno. Con ellos, añadir un campo nuevo al formulario no toca el manejador.

Armar objetos a partir de listas

Lo otro que se ve a menudo es recorrer datos y armar un objeto indexado por id:

const users = [
    { id: 1, name: 'Jaime' },
    { id: 2, name: 'María' },
    { id: 3, name: 'Pedro' }
];

const userById = {};
users.forEach(user => {
    userById[user.id] = user.name;
});

console.log(userById); // { '1': 'Jaime', '2': 'María', '3': 'Pedro' }

Aquí no hay corchetes en la definición, sino asignación con corchetes sobre un objeto que ya existe. Es lo que se hacía antes de ES2015, y sigue funcionando perfectamente. La versión con computed property names sería con reduce:

const userById = users.reduce(
    (acc, user) => ({ ...acc, [user.id]: user.name }),
    {}
);

Queda compacta, pero copia el objeto entero en cada vuelta, así que con listas grandes no la usaría. Para eso hay una opción más directa, Object.fromEntries, que recibe pares [clave, valor]:

const userById = Object.fromEntries(
    users.map(user => [user.id, user.name])
);

Y Object.defineProperty, ¿para esto?

Si alguna vez lo ves para fijar una clave que viene en una variable, funciona, pero no es lo mismo que asignar con corchetes:

const obj = {};
const fieldName = 'email';
Object.defineProperty(obj, fieldName, {
    value: 'usuario@example.com',
    enumerable: true
});

obj.email = 'otro@example.com';
console.log(obj.email); // usuario@example.com, la asignación se ignora (en modo estricto lanza un error)

defineProperty sirve para controlar los atributos de la propiedad, y por defecto writable y configurable valen false. Para poner una clave dinámica sin más, los corchetes son la forma directa y legible.

Un último cuidado

Si la clave viene de fuera (un input del usuario, una respuesta de una API), recuerda que puede ser cualquier cosa, incluido __proto__. Con la sintaxis de corchetes en la definición, como en { ...form, [e.target.name]: value }, no hay problema: __proto__ se queda como una propiedad más del objeto. El riesgo está en asignar con obj[key] = value, donde __proto__ cambia el prototipo, o en las funciones que mezclan objetos con claves que no controlas. Si vas a volcar claves desconocidas así, mejor validarlas antes contra una lista de campos permitidos.

Para cerrar

Es uno de esos detalles de JavaScript que parece una tontería, pero que cuando necesitas armar objetos con claves dinámicas (y lo necesitas más a menudo de lo que crees), te ahorra tener que hilar fino con strings y concatenación.


Tweet Send
0 Comentarios
Cargando...