O objeto Proxy introduzido no ES6 permite criar uma camada de interceptação para operações fundamentais de um objeto, como leitura, escrita, verificação de existência e exclusão de propriedades. Ele é instanciado recebendo um objeto alvo (target) e um manipulador (handler) que define as regras de interceptação.
A sintaxe básica para a criação de um prroxy é a seguinte:
const alvo = {};
const manipulador = {};
const proxy = new Proxy(alvo, manipulador);
Abaixo, demonstramos como utilizar as enterceptações (traps) get, set, has e deleteProperty para controlar o acesso e a modificação de um objeto de configuração, utilizando o objeto Reflect para garantir o comportamento padrão quando necessário.
const configAlvo = {
host: 'localhost',
porta: 8080,
chaveSecreta: 'abc123'
};
const configProxy = new Proxy(configAlvo, {
get(alvo, propriedade) {
console.log(`[GET] Acessando propriedade: ${propriedade}`);
return Reflect.get(alvo, propriedade);
},
set(alvo, propriedade, valor) {
console.log(`[SET] Modificando ${propriedade} para ${valor}`);
if (propriedade === 'porta' && (typeof valor !== 'number' || valor <= 0)) {
throw new Error('Porta inválida');
}
return Reflect.set(alvo, propriedade, valor);
},
has(alvo, propriedade) {
console.log(`[HAS] Verificando existência de: ${propriedade}`);
if (propriedade === 'chaveSecreta') return false;
return Reflect.has(alvo, propriedade);
},
deleteProperty(alvo, propriedade) {
console.log(`[DELETE] Tentativa de remover: ${propriedade}`);
if (propriedade === 'host') {
throw new Error('Não é permitido remover o host');
}
return Reflect.deleteProperty(alvo, propriedade);
}
});
console.log(configProxy.host);
configProxy.porta = 3000;
console.log('porta' in configProxy, 'chaveSecreta' in configProxy);
delete configProxy.porta;
console.log(configProxy);
Ao executar as operações acima, os logs demonstram como cada ação foi interceptada. A propriedade chaveSecreta é ocultada na verificação com o operador in, e a tentativa de remover o host geraria uma exceção.
Além do controle básico de acesso, o Proxy é amplamente utilizado para validação de dados. Podemos combinar Proxy com Reflect para garentir que um objeto respeite regras de tipagem e domínio antes de aceitar uma mutação, atuando como um esquema de validação em tempo de execução.
const aplicarValidacao = (entidade, regras) => {
return new Proxy(entidade, {
set(alvo, propriedade, valor, receptor) {
if (!Object.prototype.hasOwnProperty.call(alvo, propriedade)) {
throw new Error(`A propriedade '${propriedade}' não está definida no esquema.`);
}
if (regras[propriedade] && !regras[propriedade](valor)) {
throw new Error(`Valor inválido para '${propriedade}': ${valor}`);
}
return Reflect.set(alvo, propriedade, valor, receptor);
}
});
};
const regrasInventario = {
sku: (val) => typeof val === 'string' && val.length > 0,
quantidade: (val) => Number.isInteger(val) && val >= 0,
preco: (val) => typeof val === 'number' && val > 0
};
class ItemInventario {
constructor(sku, quantidade, preco) {
this.sku = sku;
this.quantidade = quantidade;
this.preco = preco;
return aplicarValidacao(this, regrasInventario);
}
}
const item = new ItemInventario('ITEM-001', 50, 19.99);
console.log(item);
// item.quantidade = -5; // Lança Error: Valor inválido para 'quantidade': -5
// item.sku = 123; // Lança Error: Valor inválido para 'sku': 123
// item.categoria = 'X'; // Lança Error: A propriedade 'categoria' não está definida no esquema.
Neste cenário de validação, o construtor da classe retorna o proxy configurado. Qualquer tentativa de atribuir tipos incorretos, valores fora do domínio esperado ou propriedades não mapeadas lançará exceções imediatas, garantindo a integridade e a consistência dos dados da entidade.