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 objetotarget
serã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); // Tom
No 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); // Tom
Como 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 objetoProxy
atravé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 Smith
Neste 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 Smith
Esse 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: Tom
Aqui, 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); // 37
Neste 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); // 37
Neste 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 objetoProxy
atravé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); // 22
Neste 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); // 22
Neste 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;