Introdução: Por que Proxy e Reflect Importam para Times Ágeis
Proxy e Reflect são metaprogramação em JavaScript que permitem interceptar e customizar operações fundamentais em objetos. Para times ágeis que precisam iterar rapidamente, essas APIs são ouro: elas facilitam validação, logging, lazy loading e padrões como Observer sem modificar o código original. A diferença entre um time que consegue refatorar sem medo e outro que não consegue frequentemente está em como lidam com abstrações. Vamos aprender a implementar essas práticas com segurança.
Fundamentos de Proxy: Interceptando Operações
O que é um Proxy?
Um Proxy é um objeto especial que atua como intermediário entre seu código e o objeto alvo, capturando operações como leitura, escrita e deleção de propriedades. O construtor new Proxy(target, handler) recebe um alvo e um handler (objeto com "armadilhas" ou traps).
const usuario = {
nome: 'Alice',
email: 'alice@example.com'
};
const usuarioProxy = new Proxy(usuario, {
get(target, prop) {
console.log(`Acessando propriedade: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Definindo ${prop} = ${value}`);
if (prop === 'email' && !value.includes('@')) {
throw new Error('Email inválido');
}
target[prop] = value;
return true;
}
});
usuarioProxy.nome; // "Acessando propriedade: nome" → "Alice"
usuarioProxy.email = 'invalido'; // Error: Email inválido
Casos de Uso Prático
Validação é apenas o começo. Em times ágeis, Proxies reduzem duplicação ao centralizar regras de negócio. Com lazy loading, você carrega dados sob demanda; com logging transparente, consegue debugging sem poluir classes de negócio. O handler suporta múltiplas traps: get, set, has, deleteProperty, apply, construct, e outras.
const db = {
usuarios: null // será carregado sob demanda
};
const cache = new Proxy(db, {
get(target, prop) {
if (prop === 'usuarios' && target.usuarios === null) {
console.log('Carregando usuários do banco...');
target.usuarios = [
{ id: 1, nome: 'Alice' },
{ id: 2, nome: 'Bob' }
];
}
return target[prop];
}
});
console.log(cache.usuarios);
// "Carregando usuários do banco..."
// Próximos acessos não recarregam
Reflect: A API Complementar
Entendendo Reflect
Reflect é um objeto built-in que fornece métodos estáticos para operações interceptáveis. Seu principal benefício é consistência e segurança: enquanto obj.prop pode falhar silenciosamente, Reflect.get() retorna valores previsíveis. A combinação Proxy + Reflect é o padrão profissional.
const pessoa = { nome: 'Carol', idade: 28 };
const handler = {
get(target, prop) {
// Usar Reflect garante o mesmo comportamento do JavaScript
if (prop === 'idade') {
return Reflect.get(target, prop) + 1; // Retorna próxima idade
}
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`Mudança rastreada: ${prop}`);
return Reflect.set(target, prop, value);
}
};
const pessoaProxy = new Proxy(pessoa, handler);
console.log(pessoaProxy.idade); // 29
pessoaProxy.nome = 'David'; // "Mudança rastreada: nome"
Por Que Não Apenas Usar target[prop]?
Reflect trata edge cases corretamente: propriedades não-configuráveis, descriptores personalizados e prototype chains. Em times ágeis, isso significa menos bugs em refatorações. Reflect também funciona com símbolos, o que in e delete não fazem uniformemente.
const simbolo = Symbol('privado');
const obj = {};
Object.defineProperty(obj, simbolo, {
value: 'secreto',
configurable: false
});
const proxy = new Proxy(obj, {
get(target, prop) {
// Reflect respeita propriedades não-configuráveis
return Reflect.get(target, prop);
}
});
console.log(proxy[simbolo]); // "secreto"
Padrões Avançados para Times Ágeis
Validação Reutilizável
Ao invés de espalhá-la pela codebase, centralize em um factory de validadores:
function criarProxyValidado(target, esquema) {
return new Proxy(target, {
set(target, prop, value) {
const validador = esquema[prop];
if (validador && !validador(value)) {
throw new TypeError(`Validação falhou para ${prop}`);
}
return Reflect.set(target, prop, value);
}
});
}
const schemaPedido = {
quantidade: (v) => Number.isInteger(v) && v > 0,
preco: (v) => typeof v === 'number' && v >= 0
};
const pedido = criarProxyValidado({}, schemaPedido);
pedido.quantidade = 5; // ✓
pedido.preco = -10; // Error: Validação falhou para preco
Observable Pattern com Reflect
Implemente um sistema reativo simples para notificar subscribers de mudanças:
function criarObservable(target, onChange) {
return new Proxy(target, {
set(target, prop, value) {
const anterior = Reflect.get(target, prop);
const sucesso = Reflect.set(target, prop, value);
if (sucesso && anterior !== value) {
onChange({ prop, anterior, novo: value });
}
return sucesso;
}
});
}
const estado = criarObservable({ contador: 0 }, (mudanca) => {
console.log(`${mudanca.prop}: ${mudanca.anterior} → ${mudanca.novo}`);
});
estado.contador = 1; // "contador: 0 → 1"
estado.contador = 2; // "contador: 1 → 2"
Conclusão
Proxy e Reflect são pilares de código escalável e manutenível em JavaScript. Aprendemos que Proxies interceptam operações para validação, logging e lazy loading; Reflect fornece a API correta para acessar essas operações de forma segura e previsível. Em times ágeis, esses padrões reduzem duplicação, centralizam regras e facilitam refatorações sem quebrar a codebase. Comece pequeno—valide um formulário ou implemente um observable—e evolua gradualmente.