直接覆盖子类 prototype 会丢失 constructor 及自有可枚举属性;因 js 引擎自动挂载的 constructor 被新对象覆盖,需用 object.defineproperty 显式恢复且设 enumerable: false;其他自有属性应通过 getownpropertydescriptors + defineproperties 迁移。

直接覆盖子类的 prototype 对象(比如写成 SubClass.prototype = Object.create(SuperClass.prototype))会清空原本由函数自动创建的 constructor 属性,导致实例的 constructor 指向父类甚至 Object,同时也会丢失该原型上可能存在的其他自有可枚举属性(如手动添加的静态描述字段、工具方法等)。
为什么 constructor 会丢
函数声明或表达式创建时,JS 引擎会自动在 Fn.prototype 上挂一个 constructor 属性,指向该函数本身。一旦你用新对象整体赋值给 SubClass.prototype,这个默认的 constructor 就被替换了——新对象没有该属性,自然查不到,沿原型链往上就落到 SuperClass.prototype.constructor 或更上层。
如何补回 constructor
覆盖后必须显式恢复,推荐用 Object.defineProperty,确保它不可枚举、符合原生行为:
-
不要简单写
SubClass.prototype.constructor = SubClass—— 这会让它变成可枚举属性,影响for...in等遍历 -
应该这样写:
Object.defineProperty(SubClass.prototype, 'constructor', {
value: SubClass,
enumerable: false,
writable: true,
configurable: true
});
如何保留其他自有属性
如果子类原型上原本定义过额外的自有属性(比如 SubClass.prototype.version = '1.2' 或 SubClass.prototype.describe = function(){}),在覆盖前需先提取保存,再合并到新原型中:
- 用
Object.getOwnPropertyDescriptors(SubClass.prototype)获取所有自有属性描述符(含value、enumerable等) - 创建新原型后,用
Object.defineProperties(newProto, descriptors)一次性还原 - 注意跳过
constructor(避免和上面修复冲突),或统一交由defineProperty处理
更稳妥的替代方案
避免手动覆盖 prototype,改用现代语法或辅助方式:
- 使用
class语法 +extends,编译器/运行时自动处理constructor和原型链 - 若必须函数式继承,可用
Object.setPrototypeOf(SubClass.prototype, SuperClass.prototype)替代整体赋值,保留原有属性 - 封装一个
inherit(SubClass, SuperClass)工具函数,内部完成原型链接 +constructor修复 + 描述符迁移










