从ECMAScript6开始,JavaScript就开始支持{{jsxref("Proxy")}}和 {{jsxref("Reflect")}}对象,允许你拦截并定制基础语言操作行为(比如,属性查找,赋值,枚举,函数调用,等等)。通过这两个对象,你可以在JavaScript元级别(meta level)编程。
代理(Proxies)
从ECMAScript6开始引进{{jsxref("Proxy")}}对象允许你解释特定操作和实现定制行为。例如获取一个对象的某个属性:
var handler = { get: function(target, name){ return name in target ? target[name] : 42; }}; var p = new Proxy({}, handler); p.a = 1; console.log(p.a, p.b); // 1, 42
Proxy(代理)对象定义一个target和一个handle,handle实现了一个get捕捉方法。通过这个方法,被代理的对象对于未定义的属性,不再返回undefined,而是返回一个42的数字。
更多例子参见 {{jsxref("Proxy")}} 引用页
术语
The following terms are used when talking about the functionality of proxies.
- handler
- Placeholder object which contains traps.
- traps
- The methods that provide property access. This is analogous to the concept of traps in operating systems.
- target
- Object which the proxy virtualizes. It is often used as storage backend for the proxy. Invariants (semantics that remain unchanged) regarding object non-extensibility or non-configurable properties are verified against the target.
- invariants
- Semantics that remain unchanged when implementing custom operations are called invariants. If you violate the invariants of a handler, a {{jsxref("TypeError")}} will be thrown.
Handlers and traps
The following table summarizes the available traps available to Proxy
objects. See the reference pages for detailed explanations and examples.
Handler / trap | Interceptions | Invariants |
---|---|---|
{{jsxref("Global_Objects/Proxy/handler/getPrototypeOf", "handler.getPrototypeOf()")}} | {{jsxref("Object.getPrototypeOf()")}} {{jsxref("Reflect.getPrototypeOf()")}} {{jsxref("Object/proto", "__proto__")}} {{jsxref("Object.prototype.isPrototypeOf()")}} {{jsxref("Operators/instanceof", "instanceof")}} |
getPrototypeOf method must return an object or null .If target is not extensible, Object.getPrototypeOf(proxy) method must return the same value as Object.getPrototypeOf(target) . |
{{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}} | {{jsxref("Object.setPrototypeOf()")}} {{jsxref("Reflect.setPrototypeOf()")}} |
If |
{{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}} |
{{jsxref("Object.isExtensible()")}} {{jsxref("Reflect.isExtensible()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}} |
{{jsxref("Object.preventExtensions()")}} {{jsxref("Reflect.preventExtensions()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}} |
{{jsxref("Object.getOwnPropertyDescriptor()")}} {{jsxref("Reflect.getOwnPropertyDescriptor()")}} |
A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible. A property cannot be reported as existent, if it does not exists as an own property of the target object and the target object is not extensible. A property cannot be reported as non-configurable, if it does not exists as an own property of the target object or if it exists as a configurable own property of the target object. The result of |
{{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}} |
{{jsxref("Object.defineProperty()")}} {{jsxref("Reflect.defineProperty()")}} |
A property cannot be added, if the target object is not extensible. A property cannot be added as or modified to be non-configurable, if it does not exists as a non-configurable own property of the target object. A property may not be non-configurable, if a corresponding configurable property of the target object exists. If a property has a corresponding target object property then In strict mode, a |
{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}} |
Property query: Inherited property query: {{jsxref("Reflect.has()")}} |
A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible. |
{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}} |
Property access: Inherited property access: {{jsxref("Reflect.get()")}} |
The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable data property. The value reported for a property must be undefined if the corresponding target object property is non-configurable accessor property that has undefined as its [[Get]] attribute. |
{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}} |
Property assignment: |
Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable data property. Cannot set the value of a property if the corresponding target object property is a non-configurable accessor property that has In strict mode, a |
{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}} |
Property deletion: |
A property cannot be deleted, if it exists as a non-configurable own property of the target object. |
{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}} |
Property enumeration / for...in: |
The enumerate method must return an object. |
{{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}} |
{{jsxref("Object.getOwnPropertyNames()")}} |
The result of |
{{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}} |
|
There are no invariants for the handler.apply method. |
{{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}} |
|
The result must be an |
Revocable Proxy
The {{jsxref("Proxy.revocable()")}} method is used to create a revocable Proxy
object. This means that the proxy can be revoked via the function revoke
and switches the proxy off. Afterwards, any operation leads on the proxy leads to a {{jsxref("TypeError")}}.
var revocable = Proxy.revocable({}, { get: function(target, name) { return "[[" + name + "]]"; } }); var proxy = revocable.proxy; console.log(proxy.foo); // "[[foo]]" revocable.revoke(); console.log(proxy.foo); // TypeError is thrown proxy.foo = 1 // TypeError again delete proxy.foo; // still TypeError typeof proxy // "object", typeof doesn't trigger any trap
Reflection
{{jsxref("Reflect")}} is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of the proxy handlers. Reflect
It is not a function object.
Reflect
helps with forwarding default operations from the handler to the target. Note that Reflect
is not implemented in Firefox yet.
With {{jsxref("Reflect.has()")}} for example, you get the in
operator as a function:
Reflect.has(Object, "assign"); // true
{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}