Skip to content

L61 - Proxy y Reflect

Proxy es un objeto que envuelve a otro (el target) y permite interceptar operaciones básicas como lectura, escritura, borrado y llamada de funciones. Es una forma de metaprogramación: el código puede “escuchar” y modificar el comportamiento en tiempo de ejecución.

Reflect es un objeto global con métodos que replican las operaciones internas de JavaScript. Siempre se usa junto con Proxy.

const target = { mensaje: 'Hola' };
const handler = {
get(obj, prop) {
console.log(`Leyendo propiedad: ${prop}`);
return obj[prop];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.mensaje);
// Leyendo propiedad: mensaje
// Hola
const usuario = { nombre: 'Ana', edad: 25 };
const proxyUsuario = new Proxy(usuario, {
get(target, prop) {
if (prop === 'edad') {
const edad = target[prop];
return `Tiene ${edad} años`;
}
if (!(prop in target)) {
return `Propiedad "${prop}" no existe`;
}
return target[prop];
}
});
console.log(proxyUsuario.nombre); // Ana
console.log(proxyUsuario.edad); // Tiene 25 años
console.log(proxyUsuario.email); // Propiedad "email" no existe
const producto = { precio: 100 };
const proxyProducto = new Proxy(producto, {
set(target, prop, value) {
if (prop === 'precio') {
if (typeof value !== 'number' || value <= 0) {
throw new Error('El precio debe ser un número positivo');
}
}
console.log(`Cambiando ${prop} a ${value}`);
target[prop] = value;
return true; // indica que la operación fue exitosa
}
});
proxyProducto.precio = 150; // ✓
// proxyProducto.precio = -5; // Error: El precio debe ser un número positivo
const rango = new Proxy({ min: 10, max: 50 }, {
has(target, prop) {
const num = Number(prop);
return num >= target.min && num <= target.max;
}
});
console.log(25 in rango); // true
console.log(5 in rango); // false
console.log(100 in rango); // false
const config = {
apiKey: 'abc123',
publica: 'valorPublico'
};
const proxyConfig = new Proxy(config, {
deleteProperty(target, prop) {
if (prop === 'apiKey') {
throw new Error('No puedes eliminar la API key');
}
console.log(`Eliminando ${prop}`);
return delete target[prop];
}
});
// delete proxyConfig.apiKey; // Error
delete proxyConfig.publica; // ✓

Reflect proporciona los mismos métodos que los traps de Proxy, pero como funciones independientes. Se usa dentro de los handlers para delegar la operación al target original:

const usuario = { nombre: 'Ana' };
const proxyReflect = new Proxy(usuario, {
get(target, prop, receiver) {
console.log(`Accediendo a ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Asignando ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
}
});
proxyReflect.nombre = 'Carlos';
console.log(proxyReflect.nombre);

Siempre usa Reflect dentro de los traps en lugar de manipular target directamente. Garantiza el comportamiento correcto (por ejemplo, mantener el binding de this en getters).

function conValoresPorDefecto(target, defaults) {
return new Proxy(target, {
get(target, prop) {
if (prop in target) {
return Reflect.get(target, prop);
}
return defaults[prop];
}
});
}
const config = conValoresPorDefecto({}, {
puerto: 3000,
host: 'localhost',
debug: false
});
console.log(config.puerto); // 3000 (valor por defecto)
config.puerto = 5000;
console.log(config.puerto); // 5000 (sobrescribe)
function conLogging(target) {
return new Proxy(target, {
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
if (typeof value === 'function') {
return function(...args) {
console.log(`Llamando a ${prop}(${args.join(', ')})`);
return value.apply(this, args);
};
}
return value;
}
});
}
const calculadora = conLogging({
sumar: (a, b) => a + b,
restar: (a, b) => a - b
});
calculadora.sumar(3, 4); // Llamando a sumar(3, 4) → 7
function privado(target, propiedadesPrivadas) {
return new Proxy(target, {
get(target, prop) {
if (propiedadesPrivadas.includes(prop)) {
throw new Error(`"${prop}" es privada`);
}
return Reflect.get(target, prop);
},
set(target, prop, value) {
if (propiedadesPrivadas.includes(prop)) {
throw new Error(`"${prop}" es privada`);
}
return Reflect.set(target, prop, value);
}
});
}
const user = privado({ nombre: 'Ana', _password: 'secreta' }, ['_password']);
console.log(user.nombre); // Ana
// console.log(user._password); // Error
ProxyGetters/setters
Intercepta cualquier operaciónSolo lectura/escritura
No requiere modificar el targetHay que definirlos en cada propiedad
Útil para wrapping genéricoÚtil para objetos conocidos
Más lento (overhead)Sin overhead

🎯 Regla de oro: usa Proxy cuando necesites lógica transversal (logging, validación, valores por defecto) que aplique a múltiples propiedades. Para casos puntuales, usa getters/setters.