Terminologia
- handler
- Oggetto placeholder, il quale contiene le trappole.
- traps
- I metodi che forniscono l'accesso alle proprietà. Questo è analogo al concetto di trappola nei sistemi operativi.
- target
- Oggetti, che i proxy virtualizzano (sostituiscono). Viene spesso utilizzato come back-end di archiviazione per il proxy. Le invarianti, riguardanti oggetti non estensibili o proprietà non configurabili, sono verificate prima di interagire con l'obiettivo.
Sintassi
var p = new Proxy(target, handler);
Parametri
target
- Un oggetto target che il Proxy ingloberà. Può essere un qualsiasi tipo di oggetto, array nativi inclusi, funzioni o anche altri Proxy.
handler
- Un oggetto le cui proprietà sono funzioni che definiscono i comportamenti del proxy quando un'operazione viene effettuata su di esso.
Metodi
Proxy.revocable()
- Crea un oggetto Proxy revocabile.
Metodi dell'handler object
L'oggetto handler è un oggetto placeholder, il quale contiene le trappole per il Proxy.
Esempi
Esempio base
In questo esempio base il numero 37
viene restituito come valore di default quando l'oggetto non contiene la proprietà richiesta. Viene utilizzato il get
handler.
var handler = { get: function(target, name) { return name in target ? target[name] : 37; } }; var p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37
No-op forwarding proxy
In questo esempio viene utilizzato un semplice oggetto Javascript come target, al quale il proxy inoltrerà tutte le operazioni che sono state applicate su di esso.
var target = {}; var p = new Proxy(target, {}); p.a = 37; // operazione inoltrata al target console.log(target.a); // 37. Operazione inoltrata con successo
Validation
Con un proxy, puoi facilmente validare il valore passato per un oggetto. In questo esempio viene utilizzato il set
handler.
let validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('L\'età non è un numero intero'); } if (value > 200) { throw new RangeError('L\'età sembra non essere valida'); } } // Il comportamento di default da adoperare per memorizzare il valore obj[prop] = value; return true; } }; let person = new Proxy({}, validator); person.age = 100; console.log(person.age); // 100 person.age = 'young'; // Lancia una eccezione person.age = 300; // Lancia una eccezione
Extending constructor
Una funzione proxy può facilmente estendere un costruttore con un nuovo costruttore. Questo esempio usa gli handler: construct
e apply
.
function extend(sup, base) { var descriptor = Object.getOwnPropertyDescriptor( base.prototype, 'constructor' ); base.prototype = Object.create(sup.prototype); var handler = { construct: function(target, args) { var obj = Object.create(base.prototype); this.apply(target, obj, args); return obj; }, apply: function(target, that, args) { sup.apply(that, args); base.apply(that, args); } }; var proxy = new Proxy(base, handler); descriptor.value = proxy; Object.defineProperty(base.prototype, 'constructor', descriptor); return proxy; } var Person = function(name) { this.name = name; }; var Boy = extend(Person, function(name, age) { this.age = age; }); Boy.prototype.sex = 'M'; var Peter = new Boy('Peter', 13); console.log(Peter.sex); // "M" console.log(Peter.name); // "Peter" console.log(Peter.age); // 13
Manipulating DOM nodes
Alcune volte vorresti attivare o disattivare un attributo o una classe di due elementi differenti. Qui è mostrato come è possibile farlo utilizzando il set
handler.
let view = new Proxy({ selected: null }, { set: function(obj, prop, newval) { let oldval = obj[prop]; if (prop === 'selected') { if (oldval) { oldval.setAttribute('aria-selected', 'false'); } if (newval) { newval.setAttribute('aria-selected', 'true'); } } // Il comportamento di default da adoperare per memorizzare il valore obj[prop] = newval; // Indicate success return true; } }); let i1 = view.selected = document.getElementById('item-1'); console.log(i1.getAttribute('aria-selected')); // 'true' let i2 = view.selected = document.getElementById('item-2'); console.log(i1.getAttribute('aria-selected')); // 'false' console.log(i2.getAttribute('aria-selected')); // 'true'
Value correction and an extra property
L'oggetto products
del proxy valuta il valore passato e lo converte in un array se è necessario. L'oggetto supporta anche una proprietà extra chiamata latestBrowser
, uttilizzabile sia come getter che come setter.
let products = new Proxy({ browsers: ['Internet Explorer', 'Netscape'] }, { get: function(obj, prop) { // An extra property if (prop === 'latestBrowser') { return obj.browsers[obj.browsers.length - 1]; } // Il comportamento di default per restituire il valore return obj[prop]; }, set: function(obj, prop, value) { // An extra property if (prop === 'latestBrowser') { obj.browsers.push(value); return true; } // Converte il valore se non è un array if (typeof value === 'string') { value = [value]; } // Il comportamento di default per memorizzare il valore obj[prop] = value; // Indicate success return true; } }); console.log(products.browsers); // ['Internet Explorer', 'Netscape'] products.browsers = 'Firefox'; // passa una stringa (per sbaglio) console.log(products.browsers); // ['Firefox'] <- nessun problema, il valore passato è un array products.latestBrowser = 'Chrome'; console.log(products.browsers); // ['Firefox', 'Chrome'] console.log(products.latestBrowser); // 'Chrome'
Trovare un oggetto in un array dalla sua proprietà
Questo proxy estende un array con alcune caratteristiche utiliti. Come puoi notare, puoi facilmente definire nuove proprietà senza utilizzare Object.defineProperties
. Questo esempio può essere adattato per trovare una riga di una tabella partendo dalla sua cella. In questo caso il target sarà table.rows
.
let products = new Proxy([ { name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }, { name: 'Thunderbird', type: 'mailer' } ], { get: function(obj, prop) { // Il comportamento di default per ritornare un valore; prop è di solito un numero intero if (prop in obj) { return obj[prop]; } // Ottieni il numero di prodotti; un alias di products.length if (prop === 'number') { return obj.length; } let result, types = {}; for (let product of obj) { if (product.name === prop) { result = product; } if (types[product.type]) { types[product.type].push(product); } else { types[product.type] = [product]; } } // Ottieni un prodotto dal campo name if (result) { return result; } // Ottieni un prodotto dal campo type if (prop in types) { return types[prop]; } // Ottieni i tipi di prodotto if (prop === 'types') { return Object.keys(types); } return undefined; } }); console.log(products[0]); // { name: 'Firefox', type: 'browser' } console.log(products['Firefox']); // { name: 'Firefox', type: 'browser' } console.log(products['Chrome']); // undefined console.log(products.browser); // [{ name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }] console.log(products.types); // ['browser', 'mailer'] console.log(products.number); // 3
Una lista completa di traps
Adesso, per creare una lista di trappole, per scopi didattici, proveremo a proxare un oggetto non nativo che è particolarmente adatto a questo tipo di operazioni: l' oggetto globale docCookies
creato da the "little framework" published on the document.cookie
page.
/* var docCookies = ... get the "docCookies" object here: https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support */ var docCookies = new Proxy(docCookies, { get: function (oTarget, sKey) { return oTarget[sKey] || oTarget.getItem(sKey) || undefined; }, set: function (oTarget, sKey, vValue) { if (sKey in oTarget) { return false; } return oTarget.setItem(sKey, vValue); }, deleteProperty: function (oTarget, sKey) { if (sKey in oTarget) { return false; } return oTarget.removeItem(sKey); }, enumerate: function (oTarget, sKey) { return oTarget.keys(); }, ownKeys: function (oTarget, sKey) { return oTarget.keys(); }, has: function (oTarget, sKey) { return sKey in oTarget || oTarget.hasItem(sKey); }, defineProperty: function (oTarget, sKey, oDesc) { if (oDesc && 'value' in oDesc) { oTarget.setItem(sKey, oDesc.value); } return oTarget; }, getOwnPropertyDescriptor: function (oTarget, sKey) { var vValue = oTarget.getItem(sKey); return vValue ? { value: vValue, writable: true, enumerable: true, configurable: false } : undefined; }, }); /* Test dei cookie */ console.log(docCookies.my_cookie1 = 'First value'); console.log(docCookies.getItem('my_cookie1')); docCookies.setItem('my_cookie1', 'Changed value'); console.log(docCookies.my_cookie1);
Specifiche
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Proxy' in that specification. |
Standard | Definizione iniziale. |
ECMAScript 2016 (ECMA-262) The definition of 'Proxy' in that specification. |
Standard | |
ECMAScript 2017 (ECMA-262) The definition of 'Proxy' in that specification. |
Standard | |
ECMAScript Latest Draft (ECMA-262) The definition of 'Proxy' in that specification. |
Draft |
Compatibilità tra Browser
Desktop | Mobile | Server | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Proxy | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.apply | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.construct | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.defineProperty | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.deleteProperty | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.enumerate | Chrome No support No | Edge No support No | Firefox No support 37 — 47 | IE No support No | Opera No support No | Safari No support No | WebView Android No support No | Chrome Android No support No | Firefox Android No support 37 — 47 | Opera Android No support No | Safari iOS No support No | Samsung Internet Android No support No | nodejs No support No |
handler.get | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.getOwnPropertyDescriptor | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.getPrototypeOf | Chrome Full support 49 | Edge No support No | Firefox Full support 49 | IE No support No | Opera Full support 36 | Safari No support No | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 49 | Opera Android Full support 36 | Safari iOS No support No | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.has | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.isExtensible | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 31 | IE No support No | Opera Full support 36 | Safari ? | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 31 | Opera Android Full support 36 | Safari iOS ? | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.ownKeys | Chrome Full support 49 | Edge Full support 12 | Firefox
Full support
18
| IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android
Full support
18
| Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.preventExtensions | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 22 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 22 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.set | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 18 | IE No support No | Opera Full support 36 | Safari Full support 10 | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 18 | Opera Android Full support 36 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
handler.setPrototypeOf | Chrome Full support 49 | Edge Full support 12 | Firefox Full support 49 | IE No support No | Opera Full support 36 | Safari ? | WebView Android Full support 49 | Chrome Android Full support 49 | Firefox Android Full support 49 | Opera Android Full support 36 | Safari iOS ? | Samsung Internet Android Full support 5.0 | nodejs Full support 6.0.0 |
revocable | Chrome Full support 63 | Edge Full support 12 | Firefox Full support 34 | IE No support No | Opera Full support Yes | Safari Full support 10 | WebView Android Full support 63 | Chrome Android Full support 63 | Firefox Android Full support 34 | Opera Android Full support Yes | Safari iOS Full support 10 | Samsung Internet Android Full support 8.0 | nodejs Full support 6.0.0 |
Legend
- Full support
- Full support
- No support
- No support
- Compatibility unknown
- Compatibility unknown
- Non-standard. Expect poor cross-browser support.
- Non-standard. Expect poor cross-browser support.
- Deprecated. Not for use in new websites.
- Deprecated. Not for use in new websites.
- See implementation notes.
- See implementation notes.
Vedi anche
- "Proxies are awesome" Brendan Eich presentation at JSConf (slides)
- ECMAScript Harmony Proxy proposal page and ECMAScript Harmony proxy semantics page
- Tutorial on proxies
- SpiderMonkey specific Old Proxy API
Object.watch()
is a non-standard feature but has been supported in Gecko for a long time.
Nota di licenza
Alcuni contentui (test, esempi) in questa pagina sono stati copiati o adattatu dall' ECMAScript wiki i quali contenuti sono sotto licenza CC 2.0 BY-NC-SA.