JavaScript Proxy e Reflect
Aprenda como JavaScript Proxy e Reflect funcionam: intercepte operações get, set, apply, has e delete com traps e aplique proxies para validação, controle de acesso e logging.
Os proxies de JavaScript permitem interceptar e redefinir as operações fundamentais sobre objects — leitura de uma propriedade, escrita, verificação de existência de uma chave, chamada de função e muito mais. Combinados com a API Reflect, que executa essas mesmas operações da forma "padrão", os proxies oferecem um mecanismo limpo e oficial para adicionar comportamento a objects sem modificá-los diretamente.
Este capítulo aborda o que é um proxy, as traps mais comuns (get, set, apply, has, deleteProperty), como o Reflect complementa os proxies e alguns padrões do mundo real, como validação, controle de acesso e logging.
O que é um Proxy
Um Proxy envolve um objeto alvo e um handler. O handler é um conjunto de funções chamadas traps, cada uma interceptando uma operação específica. Quando você interage com o proxy, a trap correspondente é executada em vez do comportamento padrão; se nenhuma trap for definida para uma operação, o proxy a encaminha ao alvo sem alterações.
Isso é útil sempre que você deseja adicionar comportamento transversal — logging, validação, valores padrão, regras de acesso — a um object sem alterar seu próprio código.
Sintaxe do Proxy
const proxy = new Proxy(target, handler);target: O object original cujas operações você deseja interceptar.handler: Um object cujos métodos (traps) definem como as operações se comportam.
Você então usa proxy exatamente como usaria o object original; a diferença é que suas traps são executadas no meio.
Entendendo a Trap get
A trap get intercepta leituras de propriedades no object alvo. Ela recebe o target, a chave property e o receiver (o próprio proxy). É frequentemente usada para registrar acessos, calcular propriedades dinamicamente ou retornar valores padrão para chaves ausentes.
Exemplo:
Este código configura um proxy para registrar acessos a propriedades de um object.
- Handler: Define uma trap
getpara registrar a propriedade acessada. - Object Alvo: Contém as propriedades
nameeage. - Proxy: Envolve o object
targetcom ohandler.
Quando proxy.name é acessado, registra "Getting name" e retorna "John". Isso é útil para monitorar ou depurar acessos a propriedades.
Manipulando Operações de Object com as Traps set e apply
A Trap set
A trap set pode impor regras para atribuições de propriedades, garantindo que as propriedades contenham tipos específicos ou atendam a certas condições.
Exemplo:
Este código configura um proxy para validar e registrar atribuições de propriedades em um object.
- Handler: Define uma trap
setpara verificar se a propriedadeagepossui valores válidos e registrar tentativas de defini-la. - Proxy: Envolve o object
targetcom ohandler.
Quando proxy.age é definido, verifica se é uma idade válida (0-150). Se inválida, registra um erro e lança uma exceção.
A Trap apply
O método apply em um JavaScript Proxy intercepta chamadas de função. Ele recebe três argumentos:
- target: A função original sendo chamada.
- thisArg: O valor de
thisdentro da função. - argumentsList: Um array de argumentos passados para a função.
Exemplo:
Este código configura um proxy para registrar chamadas de função e seus argumentos.
- Handler: Define uma trap
applypara registrar os argumentos quando a função é chamada. - Função:
sumsoma dois números. - Proxy: Envolve a função
sumcom ohandler.
No código fornecido, a trap apply registra os argumentos e então chama a função original usando target.apply(thisArg, argumentsList). Isso é útil para logging, depuração ou modificação dinâmica do comportamento de funções.
A Trap has
A trap has intercepta o operador in. Um uso comum é ocultar chaves "privadas" (por convenção, nomes que começam com _) para que não pareçam existir externamente.
Exemplo:
Mesmo que _secret ainda exista no alvo, o operador in retorna false, de modo que a chave fica efetivamente oculta para código que inspeciona o object.
A Trap deleteProperty
A trap deleteProperty intercepta o operador delete, permitindo proteger certas chaves de remoção. Ela deve retornar true quando a exclusão é permitida, ou lançar um erro para bloqueá-la no modo estrito.
Exemplo:
Os proxies de JavaScript são poderosos, mas devem ser usados com sabedoria. O uso excessivo de proxies pode tornar o código mais difícil de entender e manter. Observe que proxies introduzem uma pequena sobrecarga de desempenho em comparação com objects nativos.
API Reflect
Reflect é um object nativo que fornece um método para cada operação interceptável — exatamente o mesmo conjunto coberto pelas traps de proxy (Reflect.get, Reflect.set, Reflect.has, Reflect.deleteProperty, Reflect.apply, entre outros). Cada método executa a versão padrão dessa operação.
Isso faz do Reflect o complemento natural do Proxy: dentro de uma trap, normalmente você quer adicionar algum comportamento e depois deixar a operação prosseguir normalmente. Chamar o método Reflect correspondente faz exatamente isso, e ele encaminha o receiver corretamente (importante para getters/setters), o que um simples target[property] não faz. Você verá esse padrão nos exemplos práticos abaixo.
Os métodos do Reflect também retornam valores em vez de lançar exceções — por exemplo, Reflect.set retorna um boolean indicando sucesso — o que torna as operações mais previsíveis do que seus equivalentes com operadores ou Object.*.
Aqui está um tour rápido pelos principais métodos do Reflect:
1. Reflect.get()
Este método é usado para obter o valor de uma propriedade de um object.
Exemplo:
2. Reflect.set()
Este método é usado para definir o valor de uma propriedade em um object.
Exemplo:
3. Reflect.has()
Este método verifica se uma propriedade existe em um object.
Exemplo:
4. Reflect.deleteProperty()
Este método exclui uma propriedade de um object.
Exemplo:
5. Reflect.ownKeys()
Este método retorna todas as chaves de propriedades próprias de um object.
Exemplo:
6. Reflect.apply()
Este método chama uma função alvo com os argumentos fornecidos.
Exemplo:
7. Reflect.construct()
Este método é usado para criar uma nova instância de um object.
Exemplo:
Esses exemplos mostram como você pode usar os métodos Reflect para realizar operações comuns em objects de forma mais limpa e consistente.
Casos de Uso Práticos de Proxies em JavaScript
Exemplo 1: Inicialização Automática de Propriedades
Descrição: Use proxies de JavaScript para inicializar automaticamente propriedades undefined em um object. Isso pode ser útil em situações em que objects são preenchidos dinamicamente com dados ao longo do tempo, como configurações de usuário ou configurações que podem não estar definidas inicialmente.
Este código cria um proxy que verifica se uma propriedade existe em um object. Se não existir, o proxy define automaticamente um valor padrão para ela. Isso é útil para evitar erros causados por propriedades ausentes.
Exemplo 2: Controle de Acesso
Descrição: Proxies podem impor permissões de leitura ou escrita em propriedades de objects. Este exemplo demonstra um proxy que impede que certas propriedades sejam lidas ou escritas com base em regras predefinidas, o que é particularmente útil para gerenciar o acesso a dados sensíveis.
Este código protege um object controlando o acesso às suas propriedades. Ele bloqueia a leitura de 'sensitiveData' e impede a alteração de propriedades 'readOnly', ajudando a manter os dados seguros.
Exemplo 3: Logging e Depuração
Descrição: Proxies podem ser usados para registrar interações com um object, o que ajuda na depuração e no monitoramento de operações. Este exemplo cria um proxy que registra todos os gets, sets e chamadas de método realizados em um object.
Este código rastreia cada vez que alguém acessa ou altera uma propriedade do object, o que é ótimo para entender o que seu código está fazendo e quando.
Exemplo 4: Validação de Dados
Descrição: Use proxies para validação em tempo real de propriedades de objects. Isso é particularmente útil para garantir a integridade dos dados quando objects são atualizados dinamicamente em uma aplicação.
Este exemplo demonstra como usar o Proxy do JavaScript para validar e registrar alterações de propriedades. O object validator verifica se a propriedade age é um número válido entre 0 e 150. Se não for, registra um erro e lança uma exceção. Caso contrário, registra o novo valor e atualiza a propriedade. O object person usa esse validador para gerenciar sua propriedade age, garantindo que idades inválidas sejam capturadas e registradas.
Conclusão
Dominar os proxies de JavaScript permite controlar e estender o comportamento de objects sem modificá-los diretamente. Os proxies podem impor regras de validação e acesso, fornecer valores padrão e alimentar ferramentas de logging ou depuração, enquanto o Reflect mantém as operações subjacentes limpas e previsíveis. Usados com moderação, ajudam a construir aplicações mais dinâmicas e seguras.
Para se aprofundar nas operações que os proxies interceptam, consulte getters e setters de propriedades e flags e descritores de propriedades. Para o padrão de validação mostrado acima, tratamento de erros com try...catch e classes são companheiros úteis.