Sempre adorei a flexibilidade dos objetos e protótipos em JavaScript, mas, por muito tempo, senti que faltava um nível de dinamismo. O JavaScript acabou adicionando get
e set
para propriedades de objetos, o que foi uma etapa incrível, mas ainda havia espaço para melhorias.
A API JavaScript Proxy foi um aprimoramento incrível: uma interface de virtualização para controlar o comportamento de modificação de um objeto!
Formato do proxy
O proxy aceita um objeto para o qual fazer proxy e um objeto com manipuladores (“traps”) para get
, set
, has
e outros métodos de objetos comuns:
const proxy = new Proxy({}, { get: (obj, prop) => { ... }, set: (obj, prop, value) => { ... }, // more props here });
Qualquer tentativa de definir ou obter uma propriedade é executada por meio da armadilha, permitindo que o senhor execute lógica adicional, especialmente se a propriedade for indesejada, não existir ou exigir validação.
Uso básico
Vamos criar um proxy básico que retorna padrões para qualquer propriedade:
const proxy = new Proxy({}, { get: (obj, prop) => { return prop in obj ? obj[prop] : null; } }); // proxy.whatever => null
O exemplo acima ilustra que, independentemente da propriedade que o código tenta definir, a lógica do proxy pode capturar e modificá-la conforme desejado. Em vez de undefined
ser retornado para uma propriedade que não existe, o senhor pode retornar null
.
Validação
O uso mais óbvio e útil do proxy é a validação; como o senhor monitora a validação de qualquer propriedade que chega, pode manter os dados tão puros quanto possível.
const proxy = new Proxy({}, { set: (obj, prop, value) => { // Don't allow age > 100 if (prop === "age" && value > 100) { // Set to max age value = 100; } obj[prop] = value; } }); proxy.age = 120; proxy.age; // 100
O senhor pode optar por modificar os dados recebidos, como no exemplo acima, ou pode lançar um erro:
const proxy = new Proxy({}, { set: (obj, prop, value) => { // Ensure age is of type Number if (prop === "age" && isNaN(value)) { throw new Error("Invalid age value!"); return; } obj[prop] = value; } }); proxy.age = "yes"; // Uncaught error: Invalid age value!
Depuração
O senhor pode até usar o Proxy para fornecer a si mesmo pontos de depuração ou eventos para ver como e quando os valores estão sendo definidos e recuperados:
const proxy = new Proxy({}, { set: (obj, prop, value) => { console.log(`Setting ${prop} from ${obj[prop]} to ${value}`); obj[prop] = value; } }); proxy.prop = 1; proxy.prop = 2; // Setting prop from undefined to 1 // Setting prop from 1 to 2
Mesmo que o senhor não modifique nenhuma entrada ou saída, ter um gancho para alterações de valores em um objeto é incrivelmente valioso.
Formatação
Outro uso simples é a formatação dos dados que chegam ao objeto:
const proxy = new Proxy({}, { set: (obj, prop, value) => { if (prop === "age") { obj[prop] = Number(value); } } }); proxy.prop = "1"; // 1
O senhor pode formatar de cadeia de caracteres para número, de número para cadeia de caracteres ou simplesmente definir padrões.
Uso de proxies com objetos existentes
Nos exemplos fornecidos acima, usamos um objeto vazio ({}
), mas o senhor também pode usar um objeto existente:
const myObj = { x: "x", y: "y" }; // Use existing object, simply set value as is given const proxy = new Proxy(myObj, { set: (obj, prop, value) => { obj[prop] = value; } }); // proxy.x = "XXX"; proxy.x; // "XXX" myObj.x; // "XXX"
Observe que o objeto original não muda, assim como o proxy, de modo que o proxy não atua como uma “cópia”, por assim dizer.
As pessoas adoram odiar o PHP, mas uma coisa que eu adorava na linguagem eram as “propriedades mágicas” que o senhor podia espionar e reagir dinamicamente. A API Proxy parece ser a resposta do JavaScript para isso. Quanto mais o senhor puder controlar o que está entrando e saindo, melhor será o seu aplicativo!