Qualche tempo fa abbiamo ospitato un ottimo post sulle novità di EcmaScript 6. Ovviamente lo scopo di quel post era introduttivo e non ha esplorato tutte le nuove funzionalità messe a disposizione dal nuovo standard. Tra queste una delle più interessanti è l’introduzione dei Proxies. Il Proxy è uno dei design pattern classici definiti dalla Gang of Four nel loro libro “Design Patterns”. Un Proxy è un oggetto che funziona da “interfaccia” verso l’esterno di un altro oggetto. Lo schema UML del pattern è il seguente:
Proxy Pattern – Wikimedia Commons
Gli scopi di un Proxy sono molteplici e dipendono molto dal contesto. Piuttosto che fare una carrellata teorica di possibili scenari, vedremo dei casi pratici di utilizzo.
Compatiblità
Prima di buttarci a capofitto negli esempi facciamo un attimo il punto sulla compatibilità di questa nuova feature. La situazione attuale (Dicembre 2016) che potete controllare sulla pagina dedicata di caniuse.com è la seguente:
Come vedete non è possibile utilizzarlo in produzione “as is”. Si può però utilizzare questo parziale del team di Google Chrome, oppure questo plugin per Babel se utilizzate già il noto transpiler.
Logging
Nel nostro primo incontro con i Proxies ci occuperemo di Logging. Il caso d’uso è il seguente: immaginate di avere un oggetto che non ha nessuna business logic ma semplicemente proprietà. Vogliamo ora loggare tutti gli accessi a questo oggetto, in pratica vogliamo scrivere in console ogni volta che una proprietà viene letta tramite getter o scritta tramite setter.
const INITIAL_DATA = { name: 'Francesco', surname: '' }; const loggable = (target) => { const loggingHandler = { get: function (target,name) { const value = target[name]; console.log(`getting ${name}: ${value}`); return value; }, set: function (target,name,value) { console.log(`setting ${name}: ${value}`); target[name] = value; return true; } }; return new Proxy(target,loggingHandler); }; const person = loggable(INITIAL_DATA); const name = person.name; //log 'getting name: Francesco' person.surname = 'Strazzullo'; //log 'setting surname: Strazzullo'
Come vedete per creare un Proxy si wrappa un oggetto iniziale con una serie di handlers. In questo caso utilizziamo per l’appunto get
e set
che vengono rispettivamente invocati alla lettura o alla scrittura di ogni singola proprietà dell’oggetto. Notate inoltre che per il resto dell’applicazione l’oggetto non cambia il suo comportamento, quindi questa tecnica è ottima per modificare una base di codice legacy.
Observable
Passiamo ad un esempio leggermente più utile, invece che limitarci a loggare i cambiamenti di stato di un oggetto qualsiasi, vogliamo essere notificati di questi cambiamenti per poter fare delle operazioni. In pratica vogliamo creare in maniera rapida degli Observable a partire da qualsiasi oggetto JavaScript.
const INITIAL_DATA = { name: 'Francesco', surname: '' }; const observable = ({target,listener}) => { let observable; const set = (target,name,value) => { target[name] = value; listener(observable); return true; }; const handler = { set }; observable = new Proxy(target,handler); return observable; }; const listener = (person) => { console.log('person changed'); } const person = observable({ target: INITIAL_DATA, listener }); person.surname = 'Strazzullo'; //log 'person changed'
Come potete aver notato con questa tecnica potete quindi creare Observable in maniera trasparente per il resto dell’applicazione. In pratica questo può essere uno step iniziale per rendere reattiva la vostra applicazione, senza doversi incamerare il debito tecnico di una libreria complessa come RxJS.

Reactive Programming === Observe All The Things!
Validazione
Come ultimo esempio creeremo un piccolo sistema di validazione per i nostri oggetti JavaScript. Per ora ci limiteremo ad un piccolo check sul tipo.
const typeOfValidatorFactory = (expectation) => { return (value, name) => { //undefined values should be considered 'good' if(value === undefined){ return } if (typeof value !== expectation) { throw new Error(`${value.constructor.name} is not a valid for '${name}' property. Should be ${expectation}`); } } }; const validators = { 'String': typeOfValidatorFactory('string'), 'Number': typeOfValidatorFactory('number') }; const checked = ({target, definition}) => { let proxy; const set = (target, name, value) => { const currentDefinition = definition[name]; if (!currentDefinition) { throw new Error(`${name} is not a valid property for this object`); } const validator = validators[currentDefinition.name] || (() => {}); validator(value, name); target[name] = value; return true; }; const handler = { set }; proxy = new Proxy(target, handler); return proxy; }; const DEFINITION = { name: String, surname: String, age: Number }; const INITITAL_DATA = { name: 'Francesco' }; const person = checked({ target:INITITAL_DATA, definition:DEFINITION }); person.surname = 'strazzullo'; //ok person.age = 30; //ok person.age = '30' //should throw
In questo caso abbiamo un validazione davvero semplicistica, possiamo solo definire se la proprietà di un oggetto è una String
oppure un Number
. Ma partendo da questo esempio, con poche righe di codice, si può creare un engine di validazione davvero complesso. Potremmo infatti collegare regole di business ai nostri oggetti oppure creare un piccolo type system a runtime per JavaScript.
Conclusioni
Come avete potuto vedere i JavaScript Proxies sono davvero uno strumento potente. Permettono con poche righe di creare architetture davvero complesse. Soprattutto per la loro struttura sono facilmente integrabili con qualsiasi framework e con una codebase già esistente. Per chi volesse approfondire ho anche scritto un post su Medium su come creare tramite i Proxies e il Virtual Dom un piccolo framework reattivo. Alla prossima.