从ECMAScript 2015/ES6 开始,JavaScript获得对Proxy
和 Reflect
对象的支持,允许你拦截并定义基本语言操作的自定义行为(比如,属性查找,赋值,枚举,函数调用,等)。借助这两个对象,你可以在JavaScript元级别(meta level)进行编程。
代理(Proxies)
从ES6 开始采用的 Proxy
对象允许拦截某些操作操作和实现自定义行为。例如,获取一个对象上的属性:
let handler = { get: function(target, name){ return name in target ? target[name] : 42; }}; let p = new Proxy({}, handler); p.a = 1; console.log(p.a, p.b); // 1, 42
Proxy(代理)对象定义一个target和一个handle,handle实现了一个get捕捉方法。通过这个方法,被代理的对象对于未定义的属性,不再返回undefined,而是返回一个42的数字。
更多例子参见 Proxy
引用页
术语
在讨论代理Proxy
的功能时,以下这些术语将会被使用到。T
- handler
- Placeholder object which contains traps.
- traps
- 该方法提供属性访问. 和操作系统中的traps的概念类似.
- 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(常量)
- 语义上,当实现固定的操作而不发生改变的量叫做不变量(常量)。如果你违反了管理者制定的常量,将会抛出一个
TypeError
这样的错误。
Handlers and traps
以下表格中总结了Proxy对象可用的traps。详细的解释说明和例子请看reference pages。
Handler / trap | Interceptions | Invariants |
---|---|---|
handler.getPrototypeOf() |
Object.getPrototypeOf() Reflect.getPrototypeOf() __proto__ Object.prototype.isPrototypeOf() 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) . |
handler.setPrototypeOf() |
Object.setPrototypeOf() Reflect.setPrototypeOf() |
If |
handler.isExtensible() |
|
|
handler.preventExtensions() |
|
|
handler.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 |
|
handler.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 |
|
handler.has() |
Property query: Inherited property query: |
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. |
handler.get() |
Property access: Inherited property access: |
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. |
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 |
handler.deleteProperty() |
Property deletion: |
A property cannot be deleted, if it exists as a non-configurable own property of the target object. |
handler.enumerate() |
Property enumeration / for...in: |
The enumerate method must return an object. |
handler.ownKeys() |
|
The result of |
handler.apply() |
|
There are no invariants for the handler.apply method. |
handler.construct() |
|
The result must be an |
Revocable Proxy
Proxy.revocable()
方法被用来创建可撤销的代理对象。这意味着代理可以通过撤销函数来撤销并且关掉代理。后来,代理上的任意的操作都会导致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)
Reflect
是一个可提供JavaScript操作判断的内建对象。该方法和 代理句柄(proxy handlers)类似,但需要注意的是Reflect方法并不是一个函数对象。
反射帮助实现从处理器到目标的默认转发操作, 但是Reflect方法还没有在Firefox浏览器中实现
使用Reflect.has()
举例, you get the in
operator as a function:
Reflect.has(Object, "assign"); // true