15 dicembre 2021

Proxy e Reflect

Un oggetto Proxy racchiude un altro oggetto e ne intercetta le operazioni, come quelle di lettura/scrittura e molte altre; può eventualmente gestirle a modo suo oppure, in maniera del tutto trasparente, lasciare che sia l’oggetto ad occuparsene.

I proxy vengono utilizzati da molte librerie ed alcuni framework per browsers. Ne vedremo molte applicazioni pratiche in questo articolo.

Proxy

La sintassi:

let proxy = new Proxy(target, handler)
  • target – è l’oggetto da racchiudere; può essere qualsiasi cosa, anche una funzione.
  • handler – configurazione del proxy: un oggetto con “trappole”, metodi che intercettano operazioni. Ad esempio una “trappola” get per la lettura di una proprietà di target, set per la scrittura di una proprietà di target, e così via.

Per le operazioni sul proxy, se c’è un “trappola” corrispondente in handler, allora questa verrà eseguita, e il proxy potrà gestirla, altrimenti l’operazione verrà eseguita su target. Come primo esempio, creiamo un proxy senza “trappole”:

let target = {};
let proxy = new Proxy(target, {}); // handler vuoto

proxy.test = 5; // scrittura su proxy (1)
alert(target.test); // 5, la proprietà si trova su target!

alert(proxy.test); // 5, possiamo leggerla anche dal proxy (2)

for(let key in proxy) alert(key); // test, l'iterazione funziona (3)

Poiché non ci sono “trappole”, tutte le operazioni su proxy vengono inoltrate a target.

  1. Un’operazione di scrittura proxy.test= imposta il valore su target.
  2. Un’operazione di lettura proxy.test ritorna il valore da target.
  3. L’iterazione su proxy ritorna valori da target.

Come possiamo vedere, senza “trappole”, proxy è solamente un contenitore per target.

Proxy è uno speciale “oggetto esotico”. Non possiede proprietà proprie. Con un handler vuoto, le operazioni verranno automaticamente inoltrate a target.

Per attivare più funzionalità, aggiungiamo qualche “trappola”.

Cosa possiamo intercettare?

Per molte operazioni sugli oggetti, esiste un così detto “metodo interno” nella specifiche JavaScript che ne descrive il funzionamento a basso livello. Ad esempio [[Get]], il metodo interno per la lettura delle proprietà, e [[Set]], il metodo interno per la scrittura delle proprietà, e così via. Questi metodi vengono utilizzati solamente nelle specifiche, non possiamo invocarli direttamente utilizzandone il nome.

Le trappole “proxy” intercettano le invocazioni di questi metodi. Queste vengono elencate nelle specifiche Proxy e nella tabella sottostante.

Per ogni metodo interno, esiste una “trappola” in questa tabella: il nome del metodo che possiamo aggiungere al parametro handler del new Proxy per intercettare l’operazione:

Metodo Interno Handler Innescato quando…
[[Get]] get lettura di un proprietà
[[Set]] set scrittura di un proprietà
[[HasProperty]] has operatore in
[[Delete]] deleteProperty operatore delete
[[Call]] apply invocazione di funzione