Ao longo dos anos, o JavaScript tornou-se uma linguagem de programação poderosa e adaptável, evoluindo constantemente para atender às necessidades em constante mudança dos desenvolvedores.
Um de seus avanços relativamente recentes é o objeto Proxy, que capacita os programadores a criar objetos potentes e flexíveis, capazes de interceptar e modificar operações-chave em outros objetos.
Este artigo investiga os recursos do Proxy em JavaScript , abrangendo sua sintaxe, atributos, aplicativos típicos, pontos fortes, limitações, instâncias ilustrativas e abordagens recomendadas.
Um Proxy é um objeto que envolve outro objeto e intercepta operações básicas nele, como acessar, atribuir e excluir propriedades. O proxy é um aspecto crucial do JavaScript que capacita os desenvolvedores a escrever códigos mais versáteis e robustos.
O objetivo deste artigo é fornecer uma compreensão abrangente do Proxy em JavaScript, abrangendo sua sintaxe, características, benefícios, desvantagens, ilustrações e técnicas recomendadas.
O Proxy do JavaScript é uma capacidade que permite a criação de objetos capazes de modificar e customizar as operações básicas realizadas em outros objetos.
Para estabelecer um objeto Proxy, dois componentes são necessários: um objeto de destino e um objeto manipulador. O objeto alvo é aquele no qual as operações devem ser interceptadas, enquanto o objeto manipulador é responsável por manter as armadilhas, ou métodos, usados para capturar essas operações.
Aqui está um exemplo demonstrando como criar um objeto Proxy básico:
const target = { name: 'John', age: 25, }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); return target[prop]; }, }; const proxy = new Proxy(target, handler); console.log(proxy.name); // Getting property name // John
Neste exemplo, geramos um objeto de destino que possui duas características: nome e idade. Também geramos um objeto manipulador que possui um get trap para capturar qualquer esforço para ler uma propriedade no objeto de destino. Depois disso, produzimos um objeto Proxy fornecendo os objetos de destino e manipulador para o construtor Proxy. Por fim, recuperamos a propriedade name do objeto Proxy, que invoca o get trap e envia uma mensagem para o console.
Traps são métodos que interceptam operações no objeto de destino. Existem várias armadilhas que você pode usar com um objeto Proxy, incluindo get, set, has, deleteProperty e muito mais.
Aqui está uma breve visão geral de algumas das armadilhas mais usadas:
get : esta armadilha intercepta tentativas de ler uma propriedade no objeto de destino. Leva dois argumentos: o objeto de destino e a propriedade que está sendo acessada. A armadilha retorna o valor da propriedade.
set : Esta armadilha captura qualquer esforço para estabelecer uma propriedade no objeto de destino. Requer três parâmetros: o próprio objeto de destino, a propriedade que está sendo estabelecida e o valor atualizado dessa propriedade. O mecanismo tem a capacidade de alterar o valor que está sendo estabelecido, ou pode gerar um erro para impedir que o valor seja estabelecido.
has : Esta interceptação intercepta tentativas de verificar se existe uma propriedade no objeto de destino. Leva dois argumentos: o objeto de destino e a propriedade que está sendo verificada. A armadilha retorna um valor booleano indicando se a propriedade existe ou não.
deleteProperty : esta armadilha intercepta as tentativas de excluir uma propriedade do objeto de destino. Leva dois argumentos: o objeto de destino e a propriedade que está sendo excluída. A interceptação pode excluir a propriedade ou lançar um erro para evitar que a propriedade seja excluída.
Os objetos proxy possuem uma característica fascinante que permite que sejam invalidados, resultando em suas armadilhas não mais interceptando operações no objeto alvo. Para construir um objeto Proxy que pode ser invalidado, utilize a função Proxy.revocable()
.
Aqui está um exemplo:
const target = { name: 'John', age: 25, }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); return target[prop]; }, }; const {proxy, revoke} = Proxy.revocable(target, handler); console.log(proxy.name); // Getting property name // John revoke(); console.log(proxy.name); // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
Neste exemplo, criamos um objeto Proxy revogável usando o método Proxy.revocable()
. Em seguida, acessamos a propriedade name do objeto Proxy, que aciona o get trap e registra uma mensagem no console. Em seguida, revogamos o objeto Proxy usando o método revoke()
, o que significa que qualquer outra tentativa de acessar propriedades no objeto Proxy falhará.
Outra característica interessante dos objetos Proxy é que eles podem ser usados para implementar padrões de herança em JavaScript. Ao usar um objeto Proxy como protótipo de outro objeto, você pode interceptar pesquisas de propriedades e personalizar o comportamento da cadeia de protótipos.
Aqui está um exemplo:
const parent = { name: 'John', }; const handler = { get: function(target, prop) { console.log(`Getting property ${prop}`); if (!(prop in target)) { return Reflect.get(parent, prop); } return target[prop]; }, }; const child = new Proxy({}, handler); console.log(child.name); // Getting property name // John child.name = 'Bob'; console.log(child.name); // Getting property name // Bob console.log(parent.name); // John
Neste exemplo, um objeto pai é definido e possui um atributo de nome. Em seguida, criamos um objeto manipulador com um get trap que impede qualquer solicitação de leitura para as propriedades do objeto filho. A armadilha usa o método Reflect.get() para retornar ao objeto pai se a propriedade estiver ausente do objeto filho.
Em seguida, usando um objeto Proxy como protótipo e o objeto manipulador como manipulador, construímos um objeto filho. Por fim, obtemos acesso e alteramos a propriedade name do objeto filho, que aciona get e set traps e registra mensagens no console.
Um caso de uso do Proxy é armazenar em cache chamadas de funções caras. Neste exemplo, criamos um objeto Proxy que armazena em cache o resultado de uma chamada de função com base em seus argumentos.
function calculateCost(price, taxRate) { console.log('Calculating cost...'); return price * (1 + taxRate); } const cache = new Map(); const proxy = new Proxy(calculateCost, { apply(target, thisArg, args) { const key = args.join('-'); if (cache.has(key)) { console.log('Returning cached result...'); return cache.get(key); } else { const result = Reflect.apply(target, thisArg, args); cache.set(key, result); return result; } }, }); console.log(proxy(10, 0.2)); // Calculating cost... 12 console.log(proxy(10, 0.2)); // Returning cached result... 12 console.log(proxy(20, 0.2)); // Calculating cost... 24 console.log(proxy(20, 0.3)); // Calculating cost... 26 console.log(proxy(20, 0.3)); // Returning cached result... 26
Neste exemplo, definimos uma função chamada calculateCost
que pega um preço e uma taxa de imposto e retorna o custo com impostos. Em seguida, criamos um objeto de cache usando a classe Map
.
Em seguida, criamos um objeto Proxy chamado proxy
que intercepta chamadas de função usando o trap apply
. A armadilha apply
é chamada sempre que a função é chamada e recebe os argumentos da função como uma matriz. Usamos os argumentos para gerar uma chave de cache e verificamos se o resultado já está no cache. Se for, retornamos o resultado armazenado em cache. Caso contrário, calculamos o resultado e o armazenamos no cache.
Por fim, invocamos a função proxy
usando diferentes argumentos e observamos que o resultado é armazenado no cache para chamadas subsequentes com argumentos idênticos.
Outro caso de uso do Proxy é validar as propriedades do objeto. Neste exemplo, criamos um objeto Proxy que valida o comprimento de uma propriedade de string.
const user = { name: 'John', password: 'secret', }; const proxy = new Proxy(user, { set(target, prop, value) { if (prop === 'password' && value.length < 8) { throw new Error('Password must be at least 8 characters long'); } target[prop] = value; return true; }, }); console.log(proxy.name); // John console.log(proxy.password); // secret proxy.password = '12345678'; console.log(proxy.password); // 12345678 proxy.password = '123'; // Error
Neste exemplo, definimos um objeto chamado user
com um name
e uma propriedade password
. Em seguida, criamos um objeto Proxy chamado proxy
que intercepta as atribuições de propriedade usando o set
trap. A armadilha set
é chamada sempre que uma propriedade é atribuída e recebe o nome da propriedade, o novo valor e o objeto de destino.
Usamos o set
trap para verificar se a propriedade que está sendo atribuída é a propriedade password
e se o valor é menor que 8 caracteres. Se for, lançamos um erro. Caso contrário, definimos o valor da propriedade no objeto de destino.
Utilizamos o objeto proxy
para atribuir vários valores à propriedade password
e observamos que quaisquer valores com menos de 8 caracteres acionam um erro.
Outro caso de uso comum para Proxy é registrar acessos e atribuições de propriedade de objeto. Neste exemplo, criamos um objeto Proxy que registra acessos e atribuições de propriedade.
const user = { name: 'John', email: '[email protected]', }; const proxy = new Proxy(user, { get(target, prop) { console.log(`Getting ${prop} property`); return target[prop]; }, set(target, prop, value) { console.log(`Setting ${prop} property to ${value}`); target[prop] = value; return true; }, }); console.log(proxy.name); // Getting name property -> John proxy.email = '[email protected]'; // Setting email property to [email protected] console.log(proxy.email); // Getting email property -> [email protected]
Neste exemplo, definimos um objeto chamado user
com um name
e uma propriedade email
. Em seguida, criamos um objeto Proxy chamado proxy
que intercepta acessos e atribuições de propriedade usando as armadilhas get
e set
.
O get
trap é chamado sempre que uma propriedade é acessada e recebe o nome da propriedade e o objeto de destino. Neste exemplo, registramos uma mensagem no console indicando que a propriedade está sendo acessada e, em seguida, retornamos o valor da propriedade do objeto de destino.
A armadilha set
é chamada sempre que uma propriedade é atribuída e recebe o nome da propriedade, o novo valor e o objeto de destino. Neste exemplo, registramos uma mensagem no console indicando que a propriedade está sendo atribuída e, em seguida, definimos o valor da propriedade no objeto de destino.
Por fim, acessamos e atribuímos várias propriedades usando o objeto proxy
e observamos que as mensagens são registradas no console.
Comportamento personalizável : com objetos proxy, você pode interceptar e personalizar operações básicas em outros objetos, permitindo criar recursos avançados, como controle de acesso, cache e registro.
Herança : objetos proxy oferecem a capacidade de implementar padrões de herança em JavaScript, o que pode levar a um código mais versátil e escalável.
Revogável : os objetos proxy podem ser desabilitados ou revogados após serem criados, tornando-os úteis para limitar o escopo do objeto proxy ou por motivos de segurança.
Apesar de o Proxy estar conosco há muito tempo, nem todas as versões de navegadores podem oferecer suporte a essa funcionalidade.
Além disso, o uso de Proxies pode afetar negativamente o desempenho do seu aplicativo, especialmente se você os usar com muita frequência.
É importante entender o significado de usar um proxy. Ele não deve ser confiável para momentos críticos do aplicativo, como validação importante da entrada do usuário.
Fique atento às restrições : Antes de implementar um proxy em seu código, fique atento às restrições que ele impõe e como elas podem afetar a velocidade e a segurança de sua aplicação.
Os objetos proxy só devem ser usados quando absolutamente essenciais porque podem afetar o desempenho do seu código.
Teste cuidadosamente : Ao utilizar objetos Proxy, certifique-se de testar cuidadosamente e estar alerta para qualquer comportamento potencialmente inesperado.
Cumpra as normas : para tornar seu código simples de ler e manter, siga as convenções aceitas e as melhores práticas ao implementar objetos Proxy.
O artigo investiga os recursos avançados do Proxy, como padrões de herança e a capacidade de criar objetos Proxy revogáveis.
Independentemente do seu nível de experiência como desenvolvedor, compreender Proxy em JavaScript é fundamental para elevar seu código a um nível superior.
Devido à sua adaptabilidade e potência, o Proxy constitui um instrumento crítico para qualquer desenvolvedor de JavaScript que aspire a construir aplicativos complexos com facilidade.