Proxy em JavaScript
O Proxy representa um objeto que permite interceptar a execução de operações relativas a outro objeto e pode redefinir seu comportamento.
Para criar um objeto Proxy, utiliza-se o construtor Proxy():
const proxy = new Proxy(target, handler);Este construtor aceita dois parâmetros:
- target: o objeto alvo do proxy, que pode ser qualquer objeto ao qual o Proxy é aplicado.
- handler: outro objeto que define quais operações do objeto- targetserão interceptadas e redefinidas e de que maneira.
// Objeto ao qual o proxy é aplicado
const target = { name: "Tom" };
// Objeto que define como o target será redefinido
const handler = {};
// Objeto proxy
const proxy = new Proxy(target, handler);
console.log(proxy.name); // TomNo exemplo acima, target é o objeto ao qual o proxy será aplicado. Neste caso, esse objeto possui a propriedade name:
const target = { name: "Tom" };Em seguida, cria-se um manipulador handler vazio:
const handler = {};Esse objeto deve definir como o objeto target será redefinido. No entanto, deixaremos ele vazio por enquanto.
Então, cria-se o objeto Proxy, passando os objetos target e handler para o seu construtor:
const proxy = new Proxy(target, handler);A função de proxy para o objeto target significa que através do proxy podemos acessar a funcionalidade desse objeto. Neste caso, através do objeto proxy, podemos acessar a propriedade name do objeto target:
console.log(proxy.name); // TomComo utilizamos um handler vazio, que não redefine nada, o proxy se comporta exatamente como o objeto original target.
Redefinindo a Funcionalidade de um Objeto com Proxy
Acima, vimos como criar um proxy para um objeto sem, no entanto, redefinir seu comportamento. O aspecto crucial aqui é o manipulador handler, que pode interceptar acessos às propriedades do objeto proxy. Esse manipulador pode definir dois métodos: get e set.
Método get e Acesso às Propriedades do Objeto
O método get intercepta o acesso a uma propriedade ao recuperar seu valor e retorna um valor definido para essa propriedade:
const handler = {
  get: function (target, prop, receiver) {
    return algum_valor;
  },
};O método get tem três parâmetros:
- target: o próprio objeto proxyado. Esse parâmetro permite acessar a funcionalidade do objeto original.
- prop: o nome da propriedade que está sendo acessada.
- receiver: o objeto- Proxyatravés do qual o proxy está sendo executado.
Considere o seguinte exemplo:
const target = { name: "Tom" };
const handler = {
  get: function (target, prop, receiver) {
    return "Tomas Smith";
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Tomas SmithNeste caso, o manipulador handler no método get retorna a string "Tomas Smith":
get: function(target, prop, receiver) {
    return "Tomas Smith";
}Isso faz com que, ao acessar qualquer propriedade do objeto proxy, seja retornada a string mencionada:
console.log(proxy.name); // Tomas SmithEsse exemplo demonstra como interceptar o acesso a uma propriedade e redefinir a resposta de forma simples. No entanto, ainda podemos acessar o objeto original que está sendo proxyado:
const target = { name: "Tom" };
const handler = {
  get: function (target, prop) {
    return "Name: " + target.name;
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Name: TomAqui, o manipulador retorna a string "Name: " + target.name, onde target.name é o acesso à propriedade name do objeto original. A lógica para retornar o valor de uma propriedade pode ser mais complexa.
Considere um objeto mais complexo com duas propriedades:
const target = { name: "Tom", age: 37 };
const handler = {
  get: function (target, prop) {
    return target[prop];
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Tom
console.log(proxy.age); // 37Neste caso, o objeto alvo possui duas propriedades: name e age. O manipulador intercepta o acesso a essas propriedades, mas não redefine, apenas retorna os valores das propriedades originais:
return target[prop];Acesso às propriedades do objeto alvo é feito usando a sintaxe de arrays.
No entanto, podemos verificar qual propriedade está sendo acessada e realizar alguma redefinição:
const target = { name: "Tom", age: 37 };
const handler = {
  get: function (target, prop) {
    if (prop === "name") return target.name.toUpperCase();
    else return target[prop];
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // TOM
console.log(proxy.age); // 37Neste exemplo, se o acesso é à propriedade name, ou seja, à propriedade que armazena uma string, chamamos o método toUpperCase() nessa string para convertê-la em maiúsculas.
Configurando Propriedades e o Método set
O método set intercepta as operações de atribuição de valores a propriedades:
const handler = {
  set: function (target, property, value, receiver) {},
};O método set possui quatro parâmetros:
- target: o objeto original que está sendo proxyado.
- property: o nome da propriedade que está sendo acessada.
- value: o valor que está sendo atribuído.
- receiver: o objeto- Proxyatravés do qual a operação está sendo realizada.
Vamos analisar um exemplo:
const target = { name: "Tom", age: 37 };
const handler = {
  set: function (target, prop, value) {
    console.log(value);
    target[prop] = value;
  },
};
const proxy = new Proxy(target, handler);
proxy.name = "Tomas";
console.log(proxy.name); // Tomas
proxy.age = 22;
console.log(proxy.age); // 22Neste exemplo, o método set primeiro registra o valor que está sendo atribuído à propriedade e, em seguida, configura essa propriedade:
target[prop] = value;Agora, vamos modificar o exemplo para incluir uma validação:
const target = { name: "Tom", age: 37 };
const handler = {
  set: function (target, prop, value) {
    if (prop === "age" && value < 1) console.log("Idade incorreta");
    else return (target[prop] = value);
  },
};
const proxy = new Proxy(target, handler);
proxy.name = "Tomas";
console.log(proxy.name); // Tomas
proxy.age = -199; // Idade incorreta
console.log(proxy.age); // 37
proxy.age = 22;
console.log(proxy.age); // 22Neste caso, dentro do método set, verificamos se a propriedade sendo configurada é age e se o valor é menor que 1. Se estas condições forem verdadeiras, simplesmente imprimimos uma mensagem indicando que a idade é incorreta:
if (prop === "age" && value < 1) console.log("Idade incorreta");Caso contrário, atribuímos o valor à propriedade do objeto original:
else return target[prop] = value;