I proxy in JavaScript

I proxy in JavaScript, cosa sono
In JavaScript, spesso si ha la necessità di intercettare e modificare il comportamento degli oggetti, sia per aggiungere funzionalità di logging, validazione o per implementare meccanismi di sicurezza. Tradizionalmente, questo veniva fatto con getter e setter, ma questi approcci hanno limitazioni. I Proxy offrono un metodo più flessibile e potente per intercettare le operazioni sugli oggetti.
Come funzionano i Proxy
Un Proxy in JavaScript è un oggetto speciale che avvolge un altro oggetto e intercetta le sue operazioni, come lettura, scrittura e cancellazione delle proprietà. Questo avviene attraverso l’uso di handler e trap, ovvero funzioni che permettono di personalizzare il comportamento predefinito dell’oggetto.
Un esempio semplice:
const target = { message: "Ciao" };
const handler = {
get: (obj, prop) => {
console.log(`Accesso alla proprietà: ${prop}`);
return obj[prop];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.message); // Stampa "Ciao" e logga l'accesso alla proprietà
Nel esempio sopra abbiamo utilizzato una delle trap disponibili (no, non è il “genere musicale” 🙃), ovvero get che ci mette di eseguire una funzione nel momento nel quale faccciamo accesso ad una delle proprietà dell’oggetto. Quali sono le altre?
Le trap e il loro utilizzo
Le trap sono metodi speciali definiti nell’handler del Proxy. Alcune delle più comuni includono:
- get(target, prop, receiver): intercetta l’accesso alle proprietà.
- set(target, prop, value, receiver): intercetta l’assegnazione di valori.
- deleteProperty(target, prop): intercetta la cancellazione di proprietà.
- has(target, prop): intercetta l’uso dell’operatore in.
- apply(target, thisArg, argumentsList): intercetta chiamate di funzione.
- construct(target, args, newTarget): intercetta la creazione di oggetti con new.
Ogni trap permette di personalizzare profondamente il comportamento di un oggetto, rendendo i Proxy strumenti molto potenti.
Scenari d’uso dei Proxy
Gli scenari d’uso sono i più disparati, vediamono comunque alcuni in modo da comprendere meglio il loro funzionamento.
Logging automatico
Si vuole registrare automaticamente ogni accesso e modifica a un oggetto.
Esempio
const target = { name: "Alice" };
const handler = {
get: (obj, prop) => {
console.log(`Lettura di ${prop}: ${obj[prop]}`);
return obj[prop];
},
set: (obj, prop, value) => {
console.log(`Modifica di ${prop}: ${value}`);
obj[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.name; // Log: Lettura di name: Alice
proxy.name = "Bob"; // Log: Modifica di name: Bob
Il proxy intercetta l’accesso alle proprietà e la loro modifica, registrando i dettagli nella console.
Impedire l’assegnazione di valori non validi a un oggetto
const secureObject = { secret: "12345", public: "info" };
const handler = {
get: (obj, prop) => {
if (prop === "secret") {
throw new Error("Accesso negato");
}
return obj[prop];
}
};
const proxy = new Proxy(secureObject, handler);
console.log(proxy.public); // OK
console.log(proxy.secret); // Errore: Accesso negato
In questo esempio quello che vogliamo fare è valutare ed eventualmente bloccare l’accesso ad una specifica proprietà protetta.
Il proxy blocca l’accesso a secret
, ma permette di leggere public
. Non male vero?
Quando non usare i Proxy
Sebbene i Proxy siano potenti, non sono sempre la soluzione ideale. Alcuni casi in cui è meglio evitarli:
- Prestazioni critiche: L’uso dei Proxy introduce un overhead rispetto agli oggetti normali.
- Compatibilità con vecchi browser: Non tutti i browser supportano i Proxy, specialmente quelli più datati (vedi Proxy su Can I Use)
- Sostituzione semplice di metodi esistenti: Se devi solo modificare il comportamento di una funzione, potresti usare il metodo Object.defineProperty o wrapper diretti.
- Eccessiva complessità: Usare Proxy per scenari semplici può rendere il codice meno leggibile, sembra banale ma ha sempre senso valutare questi aspetti.
Caso particolare: la reattività con JavaScript
I Proxy vengono spesso utilizzati nei framework frontend, come Vue.js, per implementare la reattività dei dati. Questo significa che quando una proprietà dell’oggetto viene modificata, il sistema può automaticamente aggiornare l’interfaccia utente.
Mettiamo uno scenario dove dovete aggiornare un oggetto del vostro front-end quando un attributo di un oggetto di stato cambia. Il codice, estremamente semplificato, potrebbe essere qualcosa di questo tipo.
const state = {
count: 0
};
const handler = {
set: (obj, prop, value) => {
console.log(`Aggiornamento di ${prop}: ${value}`);
obj[prop] = value;
render(); // Simulazione di aggiornamento UI
return true;
}
};
const reactiveState = new Proxy(state, handler);
function render() {
console.log(`UI aggiornata: count = ${reactiveState.count}`); // log per test
// qua si modifica l'eventuale elemento presente nel nostro DOM
}
reactiveState.count = 1; // Log: Aggiornamento di count: 1, UI aggiornata: count = 1
reactiveState.count = 2; // Log: Aggiornamento di count: 2, UI aggiornata: count = 2
Il proxy intercetta le modifiche alla proprietà count
e chiama automaticamente la funzione render
, simulando un aggiornamento dell’interfaccia utente.
Conclusioni
I Proxy in JavaScript sono davvero potenti. Ovviamente come tutto vanno usati dove ha senso e dove effettivamente possiamo trarre vantaggio dalle loro caratteristiche.
Buon coding! 😃