父类调用子类重写方法时,如何绑定子类的this?
问题描述
我创建了一个Drawable基类,让所有Canvas对象实现这个通用API。但实例化子类时,父类构造函数中调用的setup方法里,this不包含子类的属性,导致子类属性无法访问。
简化后的代码如下:
parent.ts
export abstract class Parent { constructor(protected readonly parentProp: string) { if (autoSetup) this.setup(); } setup(): void { /* 无默认实现 */ } }
child.ts
import { Parent } from './parent.ts'; export class Child extends Parent { constructor(prop1: string, public prop2: string) { super(prop1); } override setup(): void { console.log(this.prop2); // 输出undefined,原因是什么? // 需要使用prop2执行其他逻辑 } }
问题核心:实例化Child时,父类构造函数调用setup,此时this无法访问子类的prop2属性,如何解决?
原因分析
在JavaScript/TypeScript的类继承机制中,子类实例化流程是:
- 调用子类构造函数
- 先执行
super()(父类构造函数),此时子类的属性还未完成初始化 - 父类构造执行完毕后,才会回到子类构造函数,初始化子类自身的属性
因此父类构造中调用的setup方法,此时子类属性还未被赋值,自然会得到undefined。
解决方案
方案1:子类构造完成后手动调用setup
移除父类构造中的自动setup调用,改为在子类构造函数的最后(super()之后)调用setup,确保子类属性已初始化。
修改后的父类:
// parent.ts export abstract class Parent { constructor(protected readonly parentProp: string) { // 移除构造内的setup调用 } setup(): void { /* 无默认实现 */ } }
修改后的子类:
// child.ts import { Parent } from './parent.ts'; export class Child extends Parent { constructor(prop1: string, public prop2: string) { super(prop1); this.setup(); // 子类属性初始化后调用setup } override setup(): void { console.log(this.prop2); // 正常输出prop2的值 // 后续逻辑 } }
方案2:使用静态工厂方法封装实例化流程
如果不想让调用者手动处理setup,可以在子类中定义静态工厂方法,确保实例完全初始化后再执行setup。
// child.ts import { Parent } from './parent.ts'; export class Child extends Parent { // 将构造函数设为私有,强制通过工厂方法创建实例 private constructor(prop1: string, public prop2: string) { super(prop1); } static create(prop1: string, prop2: string): Child { const instance = new Child(prop1, prop2); instance.setup(); return instance; } override setup(): void { console.log(this.prop2); // 正常输出 } } // 使用方式 const child = Child.create('prop1-value', 'prop2-value');
方案3:将子类依赖参数传递给父类(不推荐)
如果必须在父类构造中触发setup,可以把子类需要的参数通过父类构造的额外参数传递,但这种方式会让父类耦合子类的细节,破坏封装性,仅作为特殊场景下的备选方案。
// parent.ts export abstract class Parent { constructor(protected readonly parentProp: string, protected setupArgs?: any) { if (autoSetup) this.setup(); } setup(): void { /* 无默认实现 */ } }
// child.ts import { Parent } from './parent.ts'; export class Child extends Parent { public prop2: string; constructor(prop1: string, prop2: string) { super(prop1, prop2); this.prop2 = prop2; } override setup(): void { console.log(this.setupArgs); // 使用父类传递的参数 // 后续逻辑 } }
内容的提问来源于stack exchange,提问作者Aniket Kumar
相关产品推荐
相关产品推荐

