JS父类构造函数内调用的方法如何使用子类覆盖的属性值?
问题根因
该问题由JS类固定的实例初始化顺序导致:
- 父类实例字段初始化,给
this.intVal赋值为1 - 父类构造函数执行,调用
identifyClass,此时读取到的intVal为父类初始化的1 - 子类实例字段初始化,才会将
this.intVal覆盖为2 - 子类构造函数执行
父类构造执行时子类的实例字段尚未完成赋值,因此无法直接拿到覆盖后的值。
可行方案(符合限制条件)
方案1:父类构造调用加微任务包裹(优先选择,无侵入)
该方案完全不需要修改子类代码,也不要求B的用户做任何变更,父类仅做非破坏性调整即可:
class A{ intVal = 1; constructor() { // 仅将原有调用包裹在queueMicrotask中,等实例全量初始化完成后再执行 queueMicrotask(() => this.identifyClass()); } identifyClass() { console.log("I am class A", this.intVal); } } class B extends A { intVal = 2; } const x = new B(); // 输出:I am class A 2
适用场景:identifyClass的业务逻辑允许异步执行,微任务会在当前同步执行栈全部清空后触发,延迟极低,绝大多数场景都可兼容。
方案2:实例字段改原型getter(同步执行场景适用)
如果要求identifyClass必须在构造阶段同步执行,可以将父子类的实例字段改为原型getter,父类补充setter保证原有行为不变,无破坏性变更:
class A{ get intVal() { return this._intVal ?? 1; } set intVal(val) { this._intVal = val; } constructor() { this.identifyClass(); } identifyClass() { console.log("I am class A", this.intVal); } } class B extends A { get intVal() { return 2; } } const x = new B(); // 同步输出:I am class A 2
适用场景:必须同步拿到执行结果的场景,子类仅需要把原来的实例字段重写改为getter重写即可,不需要额外声明构造函数。
内容的提问来源于stack exchange,提问作者Black Mamba
相关产品推荐
相关产品推荐

