proxy 的 has trap 不自动穿透原型链,需手动遍历原型链检查自有属性;若与 get trap 权限控制不一致,会导致 in 返回 true 但访问报错。

Proxy 的 has trap 能拦截 in 操作符,但它**不自动穿透原型链做递归检测**——它只管代理对象自身是否“声称拥有”该属性,而这个“声称”由你定义。要让 in 行为符合原型链语义(即:属性不在实例上,但在原型上,也应返回 true),你得在 has 中手动模拟原型链查找。
has trap 本身不查原型,需显式遍历
has(target, prop) 默认只检查 target 自身(即 Object.prototype.hasOwnProperty.call(target, prop) 级别),不会像真实 in 那样顺着 __proto__ 一路向上找。所以若直接返回 prop in target,看似简洁,但会出错:因为 prop in target 是运行时原生行为,绕过了 Proxy 的其他 trap(比如你可能在 get 里做了权限控制,但 has 里用原生 in 就漏掉了)。
正确做法是手动遍历原型链,逐层检查:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 从
target开始,用Object.prototype.hasOwnProperty.call(obj, prop)判断自有属性 - 如果没有,用
Object.getPrototypeOf(obj)获取上一级原型,继续查 - 直到原型为
null,返回false
注意 receiver 参数与 this 绑定一致性
has trap 的第三个参数不是 receiver(那是 get/set 的),它只有两个参数:target 和 prop。但你在遍历时调用 hasOwnProperty 或 Reflect.has,必须确保上下文正确。推荐统一用 Reflect.has(obj, prop),它会尊重对象的 has trap(如果该对象本身也是 proxy);但对普通对象,Reflect.has 等价于 prop in obj,仍存在前述绕过风险。更稳妥的是结合 Object.getOwnPropertyNames + Object.getPrototypeOf 手动判断。
一个可复用的原型链 has 实现
下面这段代码能准确模拟原生 in 的行为,同时保持对 Proxy 控制权:
function hasInPrototypeChain(target, prop) {
let obj = target;
while (obj !== null) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return true;
}
obj = Object.getPrototypeOf(obj);
}
return false;
}
const handler = {
has(target, prop) {
console.log(`[HAS] checking "${prop}" in prototype chain`);
return hasInPrototypeChain(target, prop);
},
get(target, prop, receiver) {
console.log(`[GET] accessing "${prop}"`);
return Reflect.get(target, prop, receiver);
}
};
const obj = { a: 1 };
const proto = { b: 2 };
Object.setPrototypeOf(obj, proto);
const proxy = new Proxy(obj, handler);
console.log('b' in proxy); // true,并触发 [HAS] 和后续 [GET]
和 get/set 协同才能真正一致
单独修复 has 不够。如果 get trap 里做了访问限制(比如拒绝读取私有属性),但 has trap 却返回 true,就会出现 '#x' in proxy === true 但 proxy['#x'] 报错的矛盾。所以 has 的逻辑应与 get 的可访问性判断保持同步——例如都查白名单、都走同一权限函数。否则用户会感知到语义断裂。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










