可通过 New.target 判断父类是否被直接调用:若 New.target === Parent 则报错;否则校验子类是否实现约定的 init() 方法或静态属性 requiresInit,确保子类承担初始化责任。

可以通过在父类构造函数中检查 New.target 是否等于父类本身,来判断当前是否是直接调用父类(而非被子类继承后调用)。若为直接调用,则抛出错误;若为子类调用,则进一步验证子类是否实现了约定的初始化方法(如 init() 或静态属性),从而强制子类承担特定初始化责任。
利用 New.target 区分直接实例化与继承调用
New.target 在构造函数中指向 new 表达式真正调用的构造函数。父类可据此拒绝被直接实例化:
- 若
New.target === Parent,说明是new Parent(),应报错 - 若
New.target !== Parent,说明是子类调用(如new Child()),此时可继续执行校验逻辑
要求子类提供静态初始化标识或方法
仅靠 New.target 无法确保子类做了正确的事,需配合约定机制。常见做法有:
- 强制子类定义静态属性
requiresInit = true,父类构造中检查该属性是否存在且为真 - 要求子类实现实例方法
init(),并在父类构造末尾调用this.init()(需确保子类已定义该方法,可用typeof this.init === 'function'校验) - 更严格时,可在子类
constructor中显式调用super()前先完成必要字段赋值,父类则检查关键属性是否已设置(如this.id !== undefined)
结合 Reflect.construct 实现延迟校验(进阶场景)
若需在子类 constructor 执行完毕后再统一校验(例如依赖子类对 this 的完整初始化),可让父类构造函数不立即报错,而是将校验逻辑委托给一个受控的初始化钩子:
- 父类定义私有标记
this._isInitialized = false - 提供公共方法
ensureInitialized(),检查标记并抛出提示 - 子类必须在自身构造逻辑完成后调用
super.ensureInitialized()或显式设this._isInitialized = true - 也可配合
Object.defineProperty将关键属性设为只读/不可配置,倒逼子类在super()后立即赋值
这种模式不依赖运行时反射黑科技,语义清晰、调试友好,且兼容 TypeScript 类型检查——只要在子类声明中明确标注必需的属性或方法,类型系统就能提前捕获遗漏。

















