
ES6 类中定义的方法默认为不可枚举属性,因此 Object.keys(Class.prototype) 返回空数组;而传统构造函数手动赋值的原型方法默认可枚举,故能被 Object.keys() 捕获。
es6 类中定义的方法默认为不可枚举属性,因此 `object.keys(class.prototype)` 返回空数组;而传统构造函数手动赋值的原型方法默认可枚举,故能被 `object.keys()` 捕获。
在 JavaScript 中,Object.keys() 仅返回对象自身可枚举(enumerable: true)的自有字符串键名。这一行为是理解类与传统原型差异的关键切入点。
? 核心机制解析
ES6 类语法虽是“语法糖”,但其内部属性描述符(property descriptor)与传统方式存在关键区别:
- 类中定义的方法(如 whoami() { ... })会被以 enumerable: false、writable: true、configurable: true 的描述符添加到 Class.prototype 上;
- 手动赋值的原型方法(如 Proto.prototype.whoami = function() {...})则通过普通属性赋值操作添加,该操作默认启用 enumerable: true。
可通过 Object.getOwnPropertyDescriptor() 验证这一差异:
class Class { whoami() { return "class"; } }
function Proto() {}
Proto.prototype.whoami = function() { return "proto"; };
console.log(Object.getOwnPropertyDescriptor(Class.prototype, 'whoami').enumerable); // false
console.log(Object.getOwnPropertyDescriptor(Proto.prototype, 'whoami').enumerable); // true✅ 如何让类方法可枚举?
若需兼容 Object.keys() 或其他枚举场景(如序列化、调试遍历),可显式设置描述符:
class Class {
constructor() {
// 方案1:使用 Object.defineProperty(推荐用于单个方法)
Object.defineProperty(Class.prototype, 'whoami', {
value: function() { return "class"; },
enumerable: true,
writable: true,
configurable: true
});
}
}
// 方案2:批量定义(适用于初始化阶段)
Object.defineProperties(Class.prototype, {
whoami: { value: () => "class", enumerable: true },
greet: { value: () => "hello", enumerable: true }
});
console.log(Object.keys(Class.prototype)); // ["whoami", "greet"]⚠️ 注意:不建议在生产代码中随意修改 prototype 的可枚举性,因其可能破坏框架(如 Vue、MobX)的响应式追踪逻辑,或干扰 for...in、JSON.stringify() 等依赖枚举性的行为。
? 总结要点
- Object.keys() 仅收集 enumerable: true 的自有属性键名;
- ES6 类方法默认 enumerable: false —— 这是语言设计决策,旨在避免类内部方法意外暴露于通用迭代中;
- 传统构造函数原型赋值默认 enumerable: true,属历史行为遗留;
- 如确需枚举类方法,应优先使用 Object.getOwnPropertyNames()(获取所有自有键名,含不可枚举)或 Reflect.ownKeys()(含 symbol 键),而非强行修改枚举性:
console.log(Object.getOwnPropertyNames(Class.prototype)); // ["constructor", "whoami"] console.log(Reflect.ownKeys(Class.prototype)); // ["constructor", "whoami"]
掌握这一特性,有助于写出更符合 JS 原生语义、更健壮的面向对象代码。










