Proxies en JavaScript

Cómo los Proxies en JavaScript nos permiten interceptar y personalizar el comportamiento de los objetos.

proxies-javascript

A veces, trabajando con objetos en JavaScript, nos puede interesar tener un poco más de control sobre cómo se accede o se modifican sus propiedades. Quizá necesitemos validar datos, registrar accesos o incluso devolver un valor predeterminado si una propiedad no existe. Para estas situaciones, JavaScript nos ofrece una característica interesante: los Proxies.

Pensemos en un Proxy como un intermediario o una envoltura alrededor de un objeto ‘real’. Este intermediario puede interceptar operaciones que normalmente irían directas al objeto, permitiéndonos ejecutar nuestro propio código antes, después, o incluso en lugar de la operación original.

La idea central es bastante clara. Creamos un Proxy usando new Proxy(target, handler). El target es nuestro objeto original, y el handler es un objeto especial que define qué operaciones queremos interceptar y cómo. Estas ‘intercepciones’ se llaman traps (trampas).

Por ejemplo, si queremos hacer algo especial cuando alguien intenta leer una propiedad, usamos la trampa get en el handler. Veamos un caso donde devolvemos un mensaje si accedemos a la propiedad de un objeto que no existe:

const handlerGet = {
get(target, prop) {
// Comprobamos si la propiedad existe en el objeto original (target)
if (prop in target) {
return target[prop]; // Devolvemos el valor original
} else {
// Si no existe, devolvemos un mensaje personalizado
// Usamos el ejemplo que nos diste originalmente
return `La propiedad "${prop}" no existe.`;
}
}
};
const objetoOriginal = { nombre: "Documento Importante" };
// Creamos un proxy vacío inicialmente para demostrar el mensaje de "no existe"
const proxyInicialVacio = new Proxy({}, handlerGet);
console.log(proxyInicialVacio.nombre); // Output: La propiedad "nombre" no existe.
// Ahora, usemos el objeto real como target
const proxyConTarget = new Proxy(objetoOriginal, handlerGet);
console.log(proxyConTarget.nombre); // Output: Documento Importante
console.log(proxyConTarget.fecha); // Output: La propiedad "fecha" no existe.

En este código, nuestro handlerGet intercepta cualquier intento de lectura. Si la propiedad existe en el target, la devuelve; si no, devuelve nuestro mensaje personalizado. Es una forma sencilla de manejar accesos a propiedades indefinidas.

De manera similar, podemos interceptar la escritura de propiedades con la trampa set. Esto es muy útil para validaciones. Imaginemos que queremos asegurarnos de que una propiedad cantidad siempre sea un número mayor que cero.

const handlerSet = {
set(target, prop, value) {
if (prop === 'cantidad') {
if (typeof value !== 'number' || value <= 0) {
console.warn('¡Atención! La cantidad debe ser un número positivo.');
// Indicamos que la asignación no se realizó devolviendo false.
return false;
}
}
// Si la validación pasa (o no es la propiedad 'cantidad'),
// realizamos la asignación en el objeto original.
// Usamos Reflect.set para asegurar el comportamiento correcto.
Reflect.set(target, prop, value);
// Indicamos que la asignación fue exitosa.
return true;
}
};
const producto = { nombre: "Tornillos" };
const proxyValidador = new Proxy(producto, handlerSet);
proxyValidador.cantidad = 50; // Funciona
console.log(producto.cantidad); // Output: 50
proxyValidador.cantidad = -10; // Output: ¡Atención! La cantidad debe ser un número positivo.
console.log(producto.cantidad); // Output: 50 (no cambió)
proxyValidador.cantidad = "muchos"; // Output: ¡Atención! La cantidad debe ser un número positivo.
console.log(producto.cantidad); // Output: 50 (no cambió)

Aquí, el handlerSet verifica si la propiedad es cantidad y si el valor cumple nuestra condición. Si no, muestra un aviso y evita la asignación, devolviendo false.

Existen más trampas disponibles; podemos interceptar la comprobación de existencia de propiedades (has), la eliminación (deleteProperty), llamadas a funciones si el target es una función (apply), la creación de instancias si el target es un constructor (construct), y varias otras operaciones sobre los objetos.

Usos habituales de Proxies

El proxy en JavaScript es un concepto avanzado del lenguaje que permite añadir funcionalidades complejas sobre objetos. Entre ellas:

  • Validación de datos más compleja.
  • Registrar accesos o modificaciones (logging) para depuración.
  • Implementar valores por defecto de forma elegante.
  • Crear sistemas de notificación cuando un objeto cambia (patrones como observables).
  • Simular objetos complejos, como aquellos que cargan datos bajo demanda o interactúan con APIs de forma transparente.

Resumen

Los Proxies son una herramienta que nos da un control avanzado sobre cómo interactuamos con los objetos en JavaScript. Son muy útiles para tareas específicas como las que mencionamos y nos permiten añadir lógica ‘por encima’ del comportamiento normal de un objeto sin tener que modificar su estructura interna directamente, lo cual ayuda a mantener nuestro código más organizado y separado por responsabilidades.