【发布时间】:2016-08-27 09:51:59
【问题描述】:
【问题讨论】:
标签: javascript reflection ecmascript-6 es6-proxy
【问题讨论】:
标签: javascript reflection ecmascript-6 es6-proxy
Reflect 和 Proxy 的用途和功能完全不同。
MDN describes Proxy in that way:
Proxy对象用于定义基本操作(例如属性查找、赋值、枚举、函数调用等)的自定义行为。
Reflect 是一个内置对象,它为可拦截的 JavaScript 操作提供方法。方法同proxy handlers。
我知道您可能已经阅读过,所以我将使用一个示例来进一步解释它。
假设你有一个对象:
const obj = {
a: 'foo',
b: 'bar',
};
您可以像这样使用属性访问器访问属性a:
console.log(obj.a); // 'foo'
您可以使用Reflect.get() 方法来做同样的事情:
console.log(Reflect.get(obj, 'a')); // 'foo'
您还可以使用 Proxy 构造函数创建该对象的代理。我们将使用get 处理程序来拦截所有属性查找。
const proxy = new Proxy(obj, {
get(target, property) {
return property in target ? target[property] : 'default';
},
});
现在使用属性访问器或Reflect.get() 来获取未定义的属性会导致字符串'default':
console.log(proxy.c); // 'default'
console.log(Reflect.get(proxy, 'c')); // 'default'
代理和反射可以很好地协同工作。例如,您可以使用 Reflect 创建一个带有无操作 get 处理程序的代理:
new Proxy(obj, {
get: Reflect.get,
});
【讨论】:
Proxy 是一个对象的包装器,它将对它的操作转发给对象,可以选择捕获其中的一些。代理对象允许您创建一个可用于代替原始对象的对象,但它可能会重新定义基本的对象操作,例如获取、设置和定义属性。
Reflect API 旨在补充代理。 [[Get]]、[[Set]] 等内部方法只是规范,不能直接调用。 Reflect 对象使这成为可能。
所以Proxy 是一个包装器,可用于拦截对象上的[[Get]] 和[[Set]] 等基本操作,而Reflect 为我们提供了围绕这些基本操作(如[[Get]] 和[[Set]])的最小包装器,所以我们可以直接调用它们(通常从陷阱内部)。
------------ Reflect 如何补充 Proxy ------------
对于每个可由Proxy 捕获的内部方法,在Reflect 中都有一个对应的方法,其名称和参数与代理陷阱相同。 (!重要)
让我们看看这个例子来演示它是如何有用的。
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
// return target[prop];
}
});
alert(userProxy.name); // Guest
在上面的示例中,在 get 陷阱内,return Reflect.get(target, prop, receiver); 和 return target[prop]; 将打印相同的输出 (Guest)。
让我们举一个稍微复杂一点的例子来说明为什么Reflect.get 更好,以及为什么get/set 有第三个参数receiver。
让我们创建一个对象admin,它继承自user:
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop]; // (*) target = user
}
});
let admin = {
__proto__: userProxy,
_name: "Admin"
};
// Expected: Admin
alert(admin.name); // outputs: Guest (?!?)
阅读admin.name应该返回"Admin",而不是"Guest"!
问题实际上出在代理中,在(*) 行中。
admin.name 时,由于admin 对象没有这样的属性,所以搜索到它的原型。userProxy。name 属性时,其get 陷阱会触发并从原始对象中以target[prop] 在(*) 行中返回它。当prop 是getter 时,对target[prop] 的调用会在上下文this=target 中运行其代码。所以结果是来自原始对象target的this._name,即:来自user。要解决这个问题,我们需要将正确的 this 传递给 getter。 receiver,get 陷阱的第三个参数保持正确的 this 被传递给 getter(在我们的例子中是 admin)。对于常规函数,我们可以使用call/apply 绑定this 值,但我们不能对getter 执行相同的操作,因为它不是called,只是被访问了。
这是Reflect 有用的地方。请记住,对于每个可由Proxy 捕获的内部方法,在Reflect 中都有一个对应的方法,其名称和参数与代理陷阱相同。
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) { // receiver = admin
return Reflect.get(target, prop, receiver); // (*)
}
});
let admin = {
__proto__: userProxy,
_name: "Admin"
};
alert(admin.name); // Admin
详细解释请参考 Ilya Kantor 的这篇精彩文章:Proxy and Reflect
【讨论】: