receiver不可省略,因为reflect.set(target, prop, value, receiver)能确保setter中this指向当前操作对象(如admin),而非原始target(user),并正确处理属性描述符、原型链委托及getter/setter的this绑定。

为什么 receiver 在原型链赋值中不可省略
当代理对象被用作其他对象的原型时,比如 admin.__proto__ = userProxy,对 admin.name = 'Admin' 的赋值操作会触发代理的 set 陷阱。但若在陷阱里直接写 target[prop] = value,实际修改的是原始 target(即 user)上的属性,而 this 上下文丢失,导致访问器(accessor)内部的 this._name 仍指向 user,而非当前正在操作的 admin 实例。
Reflect.set 是唯一能正确传递 receiver 的方式
Reflect.set(target, prop, value, receiver) 会严格遵循 JavaScript 的 [[Set]] 内部协议,其中 receiver 就是本次赋值操作中应当作为 this 的对象。它确保:
- 访问器(set name(val))执行时,this 指向 admin 而非 user
- 属性描述符约束(如 writable: false)被严格执行,失败时返回 false,便于拦截处理
- 触发完整的原型链查找与设置流程,包括向上委托到原型上的 setter
一个可验证的对比示例
假设有以下结构:
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
let user = { _name: 'Guest', set name(v) { this._name = v; } };
let userProxy = new Proxy(user, {
set(target, key, val, receiver) {
// ✅ 正确:receiver 传入,this 在 setter 中为 admin
return Reflect.set(target, key, val, receiver);
}
});
let admin = { __proto__: userProxy };
admin.name = 'Admin'; // 此时 this._name = 'Admin' 作用于 admin 自身
若把 Reflect.set(...) 换成 target[key] = val,则 setter 中的 this 会变成 user,最终 admin._name 仍为 undefined,而 user._name 被意外改写。
配套 get 也必须用 Reflect.get(receiver)
同理,get 陷阱中若只写 target[prop],遇到继承链上的 getter(如 get name() { return this._name; })时,this 仍是 target;只有 Reflect.get(target, prop, receiver) 才能让 getter 内部的 this 正确绑定到调用方(如 admin),从而读取 admin._name 而非 user._name。










