Proxy构造函数和Reflect有什么区别?
What is the difference between Proxy constructor and Reflect?
根据记录,它们似乎具有几乎相同的功能,除了:
- 反映一次只能指定一个陷阱。
- 代理 revocable.
- 代理是构造函数。
如果上面的列表总结了所有差异,那么两者兼而有之的理由是什么?
Reflect 和 Proxy 的用途和功能完全不同。
MDN describes Proxy in that way:
The Proxy
object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
Reflect is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of 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'
Proxy 和 Reflect 可以很好地协同工作。例如,您可以使用 Reflect:
创建一个带有无操作 get
处理程序的代理
new Proxy(obj, {
get: Reflect.get,
});
Proxy
是一个对象的包装器,它将对它的操作转发给对象,可以选择捕获其中的一些操作。 Proxy 对象允许您创建一个可用于代替原始对象的对象,但它可能会重新定义基本的对象操作,例如获取、设置和定义属性。
Reflect
API 旨在补充 Proxy。 [[Get]]
、[[Set]]
等内部方法是specification-only,不能直接调用。 Reflect
对象使这成为可能。
所以 Proxy
是一个包装器,可用于拦截对象上的 [[Get]]
和 [[Set]]
等基本操作,而 Reflect
为我们提供了围绕这些基本操作的最小包装器[[Get]]
和 [[Set]]
等操作,以便我们可以直接调用它们(通常从陷阱内部)。
------------ Reflect如何补充Proxy------------
对于每个可被 Proxy
捕获的内部方法,在 Reflect
中都有一个对应的方法,其名称和参数与 Proxy 陷阱相同。 (!重要)
让我们看看这个例子来证明它是如何有用的。
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
.
让我们创建一个继承自 user
的对象 admin
:
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
应该return"Admin"
,而不是"Guest"
!
问题实际上出在代理中,在行(*)
。
- 当我们读取
admin.name
时,由于 admin
对象没有自己的 属性,搜索将转到其原型。
- 原型是
userProxy
。
- 当从代理读取
name
属性时,它的get
陷阱触发并且return从原始对象target[prop]
中删除它(*)
。当 prop
是 getter 时,对 target[prop]
的调用会在上下文 this=target
中运行其代码。所以结果是 this._name
来自原始对象 target
,即:来自 user
.
要解决此问题,我们需要将正确的 this
传递给 getter。 receiver
,get trap 的第三个参数将正确的 this
传递给 getter(在我们的例子中是 admin)。对于常规函数,我们可以使用 call/apply
绑定 this
值,但我们不能对 getter
执行相同的操作,因为它不是 called
,只是访问。
这是Reflect
有用的地方。请记住,对于每个可被 Proxy
捕获的内部方法,在 Reflect
中都有一个对应的方法,其名称和参数与代理陷阱相同。[=64=]
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
根据记录,它们似乎具有几乎相同的功能,除了:
- 反映一次只能指定一个陷阱。
- 代理 revocable.
- 代理是构造函数。
如果上面的列表总结了所有差异,那么两者兼而有之的理由是什么?
Reflect 和 Proxy 的用途和功能完全不同。
MDN describes Proxy in that way:
The
Proxy
object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
Reflect is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of 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'
Proxy 和 Reflect 可以很好地协同工作。例如,您可以使用 Reflect:
创建一个带有无操作get
处理程序的代理
new Proxy(obj, {
get: Reflect.get,
});
Proxy
是一个对象的包装器,它将对它的操作转发给对象,可以选择捕获其中的一些操作。 Proxy 对象允许您创建一个可用于代替原始对象的对象,但它可能会重新定义基本的对象操作,例如获取、设置和定义属性。
Reflect
API 旨在补充 Proxy。 [[Get]]
、[[Set]]
等内部方法是specification-only,不能直接调用。 Reflect
对象使这成为可能。
所以 Proxy
是一个包装器,可用于拦截对象上的 [[Get]]
和 [[Set]]
等基本操作,而 Reflect
为我们提供了围绕这些基本操作的最小包装器[[Get]]
和 [[Set]]
等操作,以便我们可以直接调用它们(通常从陷阱内部)。
------------ Reflect如何补充Proxy------------
对于每个可被 Proxy
捕获的内部方法,在 Reflect
中都有一个对应的方法,其名称和参数与 Proxy 陷阱相同。 (!重要)
让我们看看这个例子来证明它是如何有用的。
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
.
让我们创建一个继承自 user
的对象 admin
:
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
应该return"Admin"
,而不是"Guest"
!
问题实际上出在代理中,在行(*)
。
- 当我们读取
admin.name
时,由于admin
对象没有自己的 属性,搜索将转到其原型。 - 原型是
userProxy
。 - 当从代理读取
name
属性时,它的get
陷阱触发并且return从原始对象target[prop]
中删除它(*)
。当prop
是 getter 时,对target[prop]
的调用会在上下文this=target
中运行其代码。所以结果是this._name
来自原始对象target
,即:来自user
.
要解决此问题,我们需要将正确的 this
传递给 getter。 receiver
,get trap 的第三个参数将正确的 this
传递给 getter(在我们的例子中是 admin)。对于常规函数,我们可以使用 call/apply
绑定 this
值,但我们不能对 getter
执行相同的操作,因为它不是 called
,只是访问。
这是Reflect
有用的地方。请记住,对于每个可被 Proxy
捕获的内部方法,在 Reflect
中都有一个对应的方法,其名称和参数与代理陷阱相同。[=64=]
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