You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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))
})

原因分析

  1. 属性位置差异:dishes和accessories是定义在子类原型上的getter,而非实例自身的属性。类的getter/setter、方法默认都会挂载到类的prototype对象上,不属于实例的自有属性。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:23:26