返回
创建于
状态
公开
在 JavaScript 的 Proxy 拦截器(handler)中,receiver 是一个经常被忽视但至关重要的参数。简单来说,receiver 代表的是“最初接收操作的对象”。
核心定义
在 get(target, property, receiver) 和 set(target, property, value, receiver) 中:
- target: 被代理的目标对象。
- property: 想要访问或设置的属性名。
- receiver:
- 通常情况下,它就是 Proxy 实例本身。
- 如果 Proxy 被作为另一个对象的原型(prototype),那么
receiver指向的是那个继承自 Proxy 的对象。
为什么需要 receiver?
receiver 的存在主要是为了正确处理原型链继承中的 this 绑定问题。
如果不使用 receiver,当访问目标对象(target)中的 getter 时,getter 内部的 this 将指向 target。如果使用了 Reflect.get(target, property, receiver),则可以将 this 正确绑定到 receiver 上。
场景演示:继承中的 this 偏移
1const parent = {
2 name: 'Parent',
3 get fullName() {
4 return this.name;
5 }
6};
7
8const handler = {
9 get(target, prop, receiver) {
10 console.log('触发 GET:', prop);
11 // 情况 A: 不传 receiver (默认返回 target[prop])
12 // return target[prop];
13
14 // 情况 B: 使用 Reflect 并传入 receiver
15 return Reflect.get(target, prop, receiver);
16 }
17};
18
19const proxy = new Proxy(parent, handler);
20
21const child = {
22 name: 'Child'
23};
24
25// 让 child 继承自 proxy
26Object.setPrototypeOf(child, proxy);
27
28console.log(child.fullName);深度解析:
- 当我们调用
child.fullName时,child本身没有这个属性,于是去原型链上找。 - 原型是
proxy,触发了handler.get。 - 此时:
target是parent。receiver是child(因为是child发起的调用)。
- 如果不用 receiver:
Reflect.get(target, prop)会导致fullName内部的this指向parent,结果输出 "Parent"。 - 如果使用 receiver:
Reflect.get(target, prop, receiver)会把this强制绑定为child,结果输出 "Child"。
get 与 set 中的具体表现
| 拦截器 | receiver 的典型值 | 作用 |
|---|---|---|
| get | 发起读取操作的对象 | 确保 getter 函数内部的 this 指向调用者,而非目标原对象。 |
| set | 发起赋值操作的对象 | 确保 setter 函数内部的 this 指向调用者,防止在继承关系中赋值错位。 |
Set 中的注意事项
在 set 拦截器中,如果你使用 Reflect.set(target, prop, value, receiver),它会触发 receiver 上的 setter(如果有的话),或者直接在 receiver 对象上定义属性,而不是修改 target。这符合原生 JavaScript 的赋值行为:赋值操作通常发生在当前对象上,而不是原型上。
最佳实践
在编写 Proxy 时,为了保证代码的健壮性(特别是考虑到将来可能被继承的情况),始终建议配合 Reflect 使用 receiver:
1const handler = {
2 get(target, prop, receiver) {
3 // 逻辑处理...
4 return Reflect.get(target, prop, receiver);
5 },
6 set(target, prop, value, receiver) {
7 // 逻辑处理...
8 return Reflect.set(target, prop, value, receiver);
9 }
10};