L'oggetto Proxy è utilizzato per definire comportamenti personalizzati per operazioni fondamentali (per esempio: ricerca delle proprietà, assegnazione, enumerazione, invocazione delle funzioni, ecc.).

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 umero 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

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidEdge MobileFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Basic supportChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
revocableChrome Full support YesEdge Full support YesFirefox Full support 34IE No support NoOpera Full support YesSafari Full support 10WebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 34Opera Android Full support YesSafari iOS Full support 10Samsung Internet Android Full support Yesnodejs Full support 6.0.0
handler.applyChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.constructChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.definePropertyChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.deletePropertyChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.enumerate
DeprecatedNon-standard
Chrome No support NoEdge No support NoFirefox No support 37 — 47IE No support NoOpera No support NoSafari No support NoWebView Android No support NoChrome Android No support NoEdge Mobile No support NoFirefox Android No support 37 — 47Opera Android No support NoSafari iOS No support NoSamsung Internet Android No support Nonodejs No support No
handler.getChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.getOwnPropertyDescriptorChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.getPrototypeOfChrome No support NoEdge No support NoFirefox Full support 49IE No support NoOpera No support NoSafari No support NoWebView Android No support NoChrome Android No support NoEdge Mobile No support NoFirefox Android Full support 49Opera Android No support NoSafari iOS No support NoSamsung Internet Android No support Nonodejs Full support 6.0.0
handler.hasChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.isExtensibleChrome ? Edge ? Firefox Full support 31IE No support NoOpera ? Safari ? WebView Android ? Chrome Android ? Edge Mobile ? Firefox Android Full support 31Opera Android ? Safari iOS ? Samsung Internet Android ? nodejs Full support 6.0.0
handler.ownKeysChrome Full support 49Edge Full support 12Firefox Full support 18
Notes
Full support 18
Notes
Notes In Firefox 42, the implementation got updated to reflect the final ES2015 specification: The result is now checked if it is an array and if the array elements are either of type string or of type symbol. Enumerating duplicate own property names is not a failure anymore.
IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18
Notes
Full support 18
Notes
Notes In Firefox 42, the implementation got updated to reflect the final ES2015 specification: The result is now checked if it is an array and if the array elements are either of type string or of type symbol. Enumerating duplicate own property names is not a failure anymore.
Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.preventExtensionsChrome Full support 49Edge Full support 12Firefox Full support 22IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 22Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.setChrome Full support 49Edge Full support 12Firefox Full support 18IE No support NoOpera Full support 36Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Edge Mobile Full support YesFirefox Android Full support 18Opera Android Full support 36Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
handler.setPrototypeOfChrome ? Edge ? Firefox Full support 49IE No support NoOpera ? Safari ? WebView Android ? Chrome Android ? Edge Mobile ? Firefox Android Full support 49Opera Android ? Safari iOS ? Samsung Internet Android ? 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

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.

Tag del documento e collaboratori

Hanno collaborato alla realizzazione di questa pagina: jfet97, nuandawm, sidmonta, federicoviceconti
Ultima modifica di: jfet97,