为何Reflect.ownKeys返回结果不符预期?如何获取getter列表?
问题:Reflect.ownKeys返回结果与预期不符及获取对象getter列表的方法
问题场景
以下代码中,使用Reflect.ownKeys遍历categories中的对象时,实际输出为["_title"],但预期输出是["dishes"]和["accessories"]:
class Category { constructor( readonly _title: string, ) { } get title() { return this._title } } type Categories = typeof categories const categories = { get pets() { return new class extends Category { constructor() { super( 'Pets', ) } get dishes() { return new Category( 'Pets dishes', ) } } }, get cloth() { return new class extends Category { constructor() { super( 'Cloth', ) } get accessories() { return new Category( 'Accessories', ) } } }, } Object.entries(categories).forEach(([, category]) => { // Actual logs: // ["_title"] // ["_title"] // Expected logs: // ["dishes"] // ["accessories"] console.log(Reflect.ownKeys(category)) })
原因分析
- 属性位置差异:
dishes和accessories是定义在子类原型上的getter,而非实例自身的属性。类的getter/setter、方法默认都会挂载到类的prototype对象上,不属于实例的自有属性。 - Reflect.ownKeys的作用范围:
Reflect.ownKeys(obj)仅返回对象自身的所有属性键(包括可枚举、不可枚举,以及Symbol类型键)。这里实例自身只有_title(TypeScript的readonly参数会被编译为实例的自有属性),所以输出["_title"]。
获取对象getter列表的方法
要获取对象的getter,需要遍历其原型链上的属性描述符,筛选出带有get方法的属性:
方法一:针对单层继承场景
直接获取实例原型的属性,筛选getter:
Object.entries(categories).forEach(([, category]) => { // 获取实例对应的类原型 const proto = Object.getPrototypeOf(category); // 获取原型上的所有自有属性键 const protoKeys = Reflect.ownKeys(proto); // 筛选出带有getter的属性 const getters = protoKeys.filter(key => { const desc = Object.getOwnPropertyDescriptor(proto, key); return desc && typeof desc.get === 'function'; }); // 排除继承自Category的title getter const targetGetters = getters.filter(key => key !== 'title'); console.log(targetGetters); // 输出 ["dishes"]、["accessories"] });
方法二:支持多层继承场景
如果存在多层继承,递归遍历原型链收集所有getter:
function getObjectGetters(obj) { const getters = new Set(); let currentProto = Object.getPrototypeOf(obj); // 遍历到Object.prototype为止,避免重复收集 while (currentProto !== Object.prototype) { Reflect.ownKeys(currentProto).forEach(key => { const desc = Object.getOwnPropertyDescriptor(currentProto, key); if (desc && typeof desc.get === 'function') { getters.add(key); } }); currentProto = Object.getPrototypeOf(currentProto); } return Array.from(getters); } Object.entries(categories).forEach(([, category]) => { const allGetters = getObjectGetters(category); // 排除继承的title getter const targetGetters = allGetters.filter(key => key !== 'title'); console.log(targetGetters); // 输出 ["dishes"]、["accessories"] });
内容的提问来源于stack exchange,提问作者Pavel Perevezencev
相关产品推荐
相关产品推荐

