在线文档教程

Proxy

Proxy

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

术语

handler包含陷阱(traps)的占位符对象。

语法

var p = new Proxy(target, handler

参数

targetProxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

方法

Proxy.revocable()创建一个可撤销的Proxy对象。

handler 对象的方法

handler 对象是一个占位符对象,它包含Proxy的traps。

所有的陷阱都是可选的。如果没有定义陷阱,则默认行为是将操作转发到目标。

handler.getPrototypeOf()A trap for Object.getPrototypeOf.

一些非标准的陷阱已经过时,已被删除。

示例

基础示例

在以下简单的例子中,当对象中不存在属性名时,缺省返回数为37。例子中使用了 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

无操作转发代理

在以下例子中,我们使用了一个原生 JavaScript 对象,代理会将所有应用到它的操作转发到这个对象上。

var target = {}; var p = new Proxy(target, {} p.a = 37; // operation forwarded to the target console.log(target.a // 37. The operation has been properly forwarded

验证

通过代理,你可以轻松地验证向一个对象的传值。这个例子使用了 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' } } // The default behavior to store the value obj[prop] = value; // Indicate success return true; } }; let person = new Proxy{}, validator person.age = 100; console.log(person.age // 100 person.age = 'young'; // Throws an exception person.age = 300; // Throws an exception

扩展构造函数

方法代理可以轻松地通过一个新构造函数来扩展一个已有的构造函数。这个例子使用了constructapply

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

操作 DOM 节点

有时你希望切换两个不同的元素的属性或类名。下面展示了如何使用 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' } } // The default behavior to store the value 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'

值修正及附加属性

以下products代理会计算传值并根据需要转换为数组。这个代理对象同时支持一个叫做 latestBrowser的附加属性,这个属性可以同时作为 getter 和 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]; } // The default behavior to return the value return obj[prop]; }, set: function(obj, prop, value) { // An extra property if (prop === 'latestBrowser') { obj.browsers.push(value return true; } // Convert the value if it is not an array if (typeof value === 'string') { value = [value]; } // The default behavior to store the value 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'

通过属性查找数组中的特定对象

以下代理为数组扩展了一些实用工具。可以看到,你可以灵活地“定义”属性,而不需要使用 Object.defineProperties方法。以下例子可以用于通过单元格来查找表格中的一行。在这种情况下,target 是table.rows

let products = new Proxy([ { name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }, { name: 'Thunderbird', type: 'mailer' } ], { get: function(obj, prop) { // The default behavior to return the value; prop is usually an integer if (prop in obj) { return obj[prop]; } // Get the number of products; an alias of 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]; } } // Get a product by name if (result) { return result; } // Get products by type if (prop in types) { return types[prop]; } // Get product types 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 列表示例

出于教学目的,这里为了创建一个完整的 traps 列表示例,我们将尝试代理化一个非原生对象,这特别适用于这类操作:由 发布在 document.cookie页面上的“小型框架”创建的docCookies全局对象。

/* 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; }, } /* Cookies test */ console.log(docCookies.my_cookie1 = 'First value' console.log(docCookies.getItem('my_cookie1') docCookies.setItem('my_cookie1', 'Changed value' console.log(docCookies.my_cookie1

规范

SpecificationStatusComment
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Proxy' in that specification.StandardInitial definition.
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.Living Standard

浏览器支持

FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support49.012 (10240)18 (18)No support3610.0

FeatureAndroidChrome for AndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support5649.0(Yes)18 (18)13 (10586)3710.0