Lección 18 · 18 de 20
Proxy y Reflect · JavaScript
Intercepte lecturas, asignaciones y borrados en JavaScript con Proxy, y delegue en Reflect el comportamiento normal, con un usuario validado.

Un `Proxy` envuelve un objeto y permite interceptar las operaciones que se hacen con él: leer, asignar, borrar, comprobar con in... Cada operación interceptada es una trampa (trap) del manejador. validated devuelve un proxy de { id: 7 } con tres trampas: set, get y deleteProperty.
La trampa set se ejecuta en cada asignación, como user.age = 34. Busca una regla para esa propiedad y, si el valor no la cumple, lanza un TypeError: así, user.age = 15 se rechaza sin tocar el objeto. Si el valor es válido, escribe un registro y hace la asignación de verdad.
`Reflect` ofrece las operaciones normales del lenguaje como funciones: Reflect.set, Reflect.get, Reflect.deleteProperty, Reflect.has... Dentro de una trampa, llamarlas es la forma correcta de «hacer lo de siempre» después de añadir su propia lógica, y devuelven true o false, como espera el proxy.
La trampa get devuelve un texto por defecto, (no phone), para las propiedades que no existen, y deleteProperty protege el id. Las operaciones sin trampa, como Reflect.ownKeys o el operador in, pasan directamente al objeto original. Con esta técnica se construyen validadores, registros de cambios y la reactividad de algunas librerías de interfaz.
Código · main.js
// A Proxy wraps an object and intercepts operations through "traps".function validated(target, rules) { return new Proxy(target, { set(obj, key, value, receiver) { if (rules[key] && !rules[key](value)) { throw new TypeError(`invalid ${key}: ${JSON.stringify(value)}`); } console.log(`set ${key} = ${JSON.stringify(value)}`); return Reflect.set(obj, key, value, receiver); // the normal assignment }, get(obj, key, receiver) { return key in obj ? Reflect.get(obj, key, receiver) : `(no ${String(key)})`; }, deleteProperty(obj, key) { if (key === "id") throw new Error("id cannot be deleted"); return Reflect.deleteProperty(obj, key); }, });}const user = validated({ id: 7 }, { email: (v) => typeof v === "string" && v.includes("@"), age: (v) => Number.isInteger(v) && v >= 18,});user.email = "ana@shop.es";user.age = 34;for (const attempt of [() => (user.age = 15), () => delete user.id]) { try { attempt(); } catch (err) { console.log(`${err.name}: ${err.message}`); }}console.log(user.email, user.age, user.phone);console.log("Reflect.ownKeys:", Reflect.ownKeys(user).join(", "));console.log("Has email?", Reflect.has(user, "email"), "| has phone?", "phone" in user);Salida · node main.js
set email = "ana@shop.es" set age = 34 TypeError: invalid age: 15 Error: id cannot be deleted ana@shop.es 34 (no phone) Reflect.ownKeys: id, email, age Has email? true | has phone? false
Para recordar
- Un
Proxyintercepta operaciones sobre un objeto mediante trampas. set,getydeletePropertyson trampas habituales.Reflecthace la operación normal y es el complemento natural de cada trampa.- Las operaciones sin trampa llegan sin cambios al objeto original.
Ejercicio
Añada una trampa has que oculte al operador in las propiedades que empiecen por _, y haga que email no se pueda cambiar una vez asignado.