--- title: Proxy slug: Web/JavaScript/Reference/Global_Objects/Proxy translation_of: Web/JavaScript/Reference/Global_Objects/Proxy ---
O objeto Proxy é usado para definir comportamentos customizados para operações fundamentais (por exemplo, pesquisa de propriedade, atribuição, enumeração, invocação de função, etc.).
var p = new Proxy(target, handler);
target
Proxy
.handler
Proxy
revogável.O objeto manipulado é um objeto reservado que contém traps para Proxy.
Neste exemplo simples, o número 37
é retornado como o valor padrão quando o nome da propriedade não está no objeto. Usa-se o manipulador get
.
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
Neste exemplo, estamos usando um objeto JavaScript nativo ao qual nosso proxy irá encaminhar todas as operações que são aplicadas para ele.
var target = {}; var p = new Proxy(target, {}); p.a = 37; // Operação encaminhada para o alvo console.log(target.a); // 37. A operação foi devidamente encaminhada
Com um Proxy
, você pode validar facilmente o valor passado para um objeto. Este exemplo usa o manipulador set
.
let validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value > 200) { throw new RangeError('The age seems invalid'); } } //O comportamento padrão para armazenar o valor obj[prop] = value; // Indique o sucesso return true; } }; let person = new Proxy({}, validator); person.age = 100; console.log(person.age); // 100 person.age = 'young'; // Lança uma exceção person.age = 300; // Lança uma exceção
Um proxy de função poderia facilmente estender um construtor com um novo construtor. Este exemplo usa os manipuladores 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
Às vezes, você deseja alternar o atributo ou o nome da classe de dois elementos diferentes. Veja como usar o manipulador set
.
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'); } } // O comportamento para armazenar o valor padrão obj[prop] = newval; // Indica o sucesso 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'
O objeto de proxy produtos
avalia o valor passado e converte-o em uma matriz, se necessário. O objeto também suporta uma propriedade adicional chamada latestBrowser
tanto em getters como em setters.
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]; } // O comportamento para armazenar o valor padrão return obj[prop]; }, set: function(obj, prop, value) { // An extra property if (prop === 'latestBrowser') { obj.browsers.push(value); return true; } // Converta o valor se não for uma matriz if (typeof value === 'string') { value = [value]; } // O comportamento para armazenar o valor padrão obj[prop] = value; // Indicate success return true; } }); console.log(products.browsers); // ['Internet Explorer', 'Netscape'] products.browsers = 'Firefox'; // pass a string (by mistake) console.log(products.browsers); // ['Firefox'] <- no problem, the value is an array products.latestBrowser = 'Chrome'; console.log(products.browsers); // ['Firefox', 'Chrome'] console.log(products.latestBrowser); // 'Chrome'
Esta proxy estende uma matriz com alguns recursos de utilidade. Como você vê, você pode "definir" propriedades flexíveis sem usar Object.defineProperties
. Este exemplo pode ser adaptado para encontrar uma linha de tabela por sua célula. Nesse caso, o alvo será table.rows
let products = new Proxy([ { name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }, { name: 'Thunderbird', type: 'mailer' } ], { get: function(obj, prop) { // O comportamento para retornar o valor; Prop geralmente é um inteiro if (prop in obj) { return obj[prop]; } // Obter o número de produtos; Com 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]; } } // Obtém um produto por nome if (result) { return result; } // Obtém produtos por tipo if (prop in types) { return types[prop]; } // Obtém tipos de produto 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
traps
Agora, para criar uma lista completa de amostra de traps
, para fins didáticos, tentaremos propor um objeto não nativo que seja particularmente adequado para este tipo de operação: o objeto global docCookies
criado por a "little framework" publicada na páginadocument.cookie
.
/* 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; }, }); /* Teste Cookies */ console.log(docCookies.my_cookie1 = 'First value'); console.log(docCookies.getItem('my_cookie1')); docCookies.setItem('my_cookie1', 'Changed value'); console.log(docCookies.my_cookie1);
Especificações | Status | Comentário |
---|---|---|
{{SpecName('ES2015', '#sec-proxy-objects', 'Proxy')}} | {{Spec2('ES2015')}} | Definição Inicial. |
{{SpecName('ESDraft', '#sec-proxy-objects', 'Proxy')}} | {{Spec2('ESDraft')}} | ? |
{{Compat("javascript.builtins.Proxy", 2)}}
Object.getPrototypeOf(proxy)
retorna incondicionalmente um Object.getPrototypeOf(target)
, porque a trap ES2015 getPrototypeOf não foi implementada ({{bug(795904)}}, {{bug(888969)}}).Array.isArray(proxy)
retorna incondicionalmente um Array.isArray(target)
({{bug(1096753)}}, {{bug(1111785)}}).Object.prototype.toString.call(proxy)
retorna incondicionalmente Object.prototype.toString.call(target)
, porque ES2015 Symbol.toStringTag não foi implementado ({{bug(1114580)}}).Nota de licença
Alguns conteúdos (texto, exemplos) nesta página foram copiados ou adaptados do ECMAScript wiki que contém à licença de conteúdo CC 2.0 BY-NC-SA .