--- title: 元编程 slug: Web/JavaScript/Guide/Meta_programming tags: - Guide - JavaScript - Proxy - Reflect translation_of: Web/JavaScript/Guide/Meta_programming ---
从ECMAScript 2015 开始,JavaScript 获得了 {{jsxref("Proxy")}} 和 {{jsxref("Reflect")}} 对象的支持,允许你拦截并定义基本语言操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。借助这两个对象,你可以在 JavaScript 元级别进行编程。
在 ECMAScript 6 中引入的 {{jsxref("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 对象定义了一个目标(这里是一个空对象)和一个实现了 get 陷阱的 handler 对象。这里,代理的对象在获取未定义的属性时不会返回 undefined,而是返回 42。
更多例子参见 {{jsxref("Proxy")}} 页面 。
在讨论代理的功能时会用到以下术语。
以下表格中总结了 Proxy 对象可用的陷阱。详细的解释和例子请看参考页。
| 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")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}} | {{jsxref("Object.setPrototypeOf()")}} {{jsxref("Reflect.setPrototypeOf()")}} |
如果 target 不可扩展,prototype 参数必须与Object.getPrototypeOf(target)的值相同。 |
| {{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}} | {{jsxref("Object.isExtensible()")}} {{jsxref("Reflect.isExtensible()")}} |
Object.isExtensible(proxy) 必须返回和Object.isExtensible(target)一样的值。 |
| {{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}} | {{jsxref("Object.preventExtensions()")}} {{jsxref("Reflect.preventExtensions()")}} |
如果Object.isExtensible(proxy) 值为 false,Object.preventExtensions(proxy) 只返回true。 |
| {{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}} | {{jsxref("Object.getOwnPropertyDescriptor()")}} {{jsxref("Reflect.getOwnPropertyDescriptor()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}} | {{jsxref("Object.defineProperty()")}} {{jsxref("Reflect.defineProperty()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}} | Property query: foo in proxyInherited property query: foo in Object.create(proxy){{jsxref("Reflect.has()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}} | Property access: proxy[foo]and proxy.barInherited property access: Object.create(proxy)[foo]{{jsxref("Reflect.get()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}} | Property assignment: proxy[foo] = bar and proxy.foo = barInherited property assignment: Object.create(proxy)[foo] = bar{{jsxref("Reflect.set()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}} | Property deletion: delete proxy[foo] and delete proxy.foo{{jsxref("Reflect.deleteProperty()")}} |
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: for (var name in proxy) {...}{{jsxref("Reflect.enumerate()")}} |
The enumerate method must return an object. |
| {{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}} | {{jsxref("Object.getOwnPropertyNames()")}} {{jsxref("Object.getOwnPropertySymbols()")}} {{jsxref("Object.keys()")}} {{jsxref("Reflect.ownKeys()")}} |
|
| {{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}} | proxy(..args){{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}} {{jsxref("Reflect.apply()")}} |
There are no invariants for the handler.apply method. |
| {{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}} | new proxy(...args){{jsxref("Reflect.construct()")}} |
结果一定是一个Object。 |
Proxy{{jsxref("Proxy.revocable()")}} 方法被用来创建可撤销的 Proxy 对象。这意味着 proxy 可以通过 revoke 函数来撤销,并且关闭代理。此后,代理上的任意的操作都会导致{{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
{{jsxref("Reflect")}} 是一个内置对象,它提供了可拦截 JavaScript 操作的方法。该方法和{{jsxref("Global_Objects/Proxy/handler","代理句柄")}}类似,但 Reflect 方法并不是一个函数对象。
Reflect 有助于将默认操作从处理程序转发到目标。
以 {{jsxref("Reflect.has()")}} 为例,你可以将 in 运算符作为函数:
Reflect.has(Object, "assign"); // true
apply 函数在 ES5 中,我们通常使用 {{jsxref("Function.prototype.apply()")}} 方法调用一个具有给定 this 值和 arguments 数组(或类数组对象)的函数。
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
使用 {{jsxref("Reflect.apply")}},这变得不那么冗长和容易理解:
Reflect.apply(Math.floor, undefined, [1.75]);
// 1;
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
// "hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index;
// 4
Reflect.apply(''.charAt, 'ponies', [3]);
// "i"
使用 {{jsxref("Object.defineProperty")}},如果成功返回一个对象,否则抛出一个 {{jsxref("TypeError")}},你将使用 {{jsxref("Statements/try...catch","try...catch")}} 块来捕获定义属性时发生的任何错误。因为 {{jsxref("Reflect.defineProperty")}} 返回一个布尔值表示的成功状态,你可以在这里使用 {{jsxref("Statements/if...else","if...else")}} 块:
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}