JavaScript/TypeScript类代理处理器内如何获取属性描述符
当前代码中Proxy get陷阱接收的target是经过super()初始化的类实例本身。通过类语法声明的方法、访问器,默认会挂载在对应类的原型对象上,不属于实例自身的自有属性。直接对实例调用Object.getOwnPropertyDescriptor查找方法属性时,自然会返回undefined。
你代码中使用的Reflect.get(target, propertyKey, receiver)可以正常返回方法结果,本质是该API的内部实现已经自动完成了原型链的遍历查找,而你手动查询描述符的逻辑没有覆盖这层遍历。
顺着原型链逐级查找属性描述符是完全可行的稳定方案,不需要额外引入依赖,也不需要重构Proxy的挂载逻辑。
基础实现
编写工具函数从传入的实例开始,沿着原型链逐级调用Object.getOwnPropertyDescriptor,直到找到匹配的属性描述符,遍历到原型链顶端(null)仍未找到则返回undefined,参考实现:
function getPropertyDescriptorFromChain(target: any, key: PropertyKey): PropertyDescriptor | undefined { let current = target while (current) { const desc = Object.getOwnPropertyDescriptor(current, key) if (desc) return desc current = Object.getPrototypeOf(current) } return undefined }
替换handler中原先查询描述符的代码即可:
const handler: ProxyHandler<any> = { get(target, propertyKey, receiver) { const desc = getPropertyDescriptorFromChain(target, propertyKey) console.log(desc) // 可正确拿到method对应的函数类型描述符 return Reflect.get(target, propertyKey, receiver) } }
类型分支判断
拿到描述符后,可直接按属性类型走对应处理逻辑:
- 若描述符包含
get/set字段,属于访问器属性 - 若描述符的
value字段为函数类型,属于类方法 - 其余无
get/set、非函数值的描述符,属于普通数据属性
性能优化
原型链遍历存在一定重复开销,可通过WeakMap做缓存,把已经查找过的target + propertyKey对应的描述符结果存储起来,避免每次访问属性都重复遍历:
const descCache = new WeakMap<object, Map<PropertyKey, PropertyDescriptor | undefined>>() function getPropertyDescriptorFromChain(target: any, key: PropertyKey) { // 优先读取缓存 const targetCache = descCache.get(target) if (targetCache?.has(key)) return targetCache.get(key) // 执行原型链遍历查找 let current = target let found: PropertyDescriptor | undefined while (current) { const desc = Object.getOwnPropertyDescriptor(current, key) if (desc) { found = desc break } current = Object.getPrototypeOf(current) } // 写入缓存 if (!targetCache) { descCache.set(target, new Map()) } descCache.get(target)!.set(key, found) return found }
不要为了查询原型上的属性把Proxy挂载到类原型上,这种写法会影响所有继承该类的实例,甚至会污染原型对象的查找逻辑,引入更多边界问题。保持当前Proxy包裹实例的写法是更安全的,只需要补全描述符查找的原型链遍历逻辑即可。
内容的提问来源于stack exchange,提问作者mlhaufe

