提问人:Aniket Kumar 提问时间:11/14/2023 最后编辑:Aniket Kumar 更新时间:11/14/2023 访问量:31
在 Javascript / TypeScript 中从父类调用被覆盖的方法时如何绑定子类“this”?
How do bind subclass 'this' when calling an overridden method from the parent class in Javascript / TypeScript?
问:
我正在尝试使用 HTML5 Canvas API 实现乒乓球游戏。为了保持井井有条,我创建了一个(基)类,并让每个 Canvas 对象都实现了这个通用 API,包括 .问题是传递给子类的子类没有任何子类的属性。如何解决此问题?Drawable
Paddle
this
我已经提取了问题,这是相同代码的简化版本。
// File: parent.ts
export abstract class Parent {
constructor(protected readonly parentProp: string) {
if (autoSetup) this.setup();
}
setup(): void { /* no default setup required */ }
}
// File: 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, why?
// do other things with prop2
}
}
现在,当 实例化时,类构造函数调用设置并将其传递给它自己的 .如何使它也包括子类属性?Child
Parent
this
this
我觉得我错过了一些我不知道解决这个问题的关键技术。如果我不能解决这个问题,那么唯一的方法就是重新考虑整个代码并将设置从超类中提取出来,这将破坏基于 OO 的方法的目的。
提前感谢您的任何帮助!:)
答:
不幸的是,您尝试做的事情是不可能的。类继承在 JS 中的工作方式是,在调用完成运行之前,派生类的对象不会初始化(另请参阅为什么在 super()
之前不允许这样做
?this
super()
调用 时,调用类的构造函数,而构造函数又调用类的方法。正在运行时,仍未完成,这就是为什么 still 未在 的上下文中初始化的原因。由于此时仅在 的上下文中初始化,因此只能访问由 的构造函数初始化的字段(例如 )。这就是为什么你不能使它成为在调用时包含子类属性的原因。super()
Parent
setup()
Child
setup()
super()
this
Child
this
Parent
Parent
this.parentProp
this
setup()
需要注意的另一点是 TypeScript 的参数属性是如何工作的。从本质上讲,它只是语法糖,相当于声明字段并在调用后立即初始化它们。这意味着您的代码等效于以下内容:super()
export abstract class Parent {
protected readonly parentProp: string;
constructor(parentProp: string) {
this.parentProp = parentProp;
if (autoSetup) this.setup();
}
setup(): void { /* no default setup required */ }
}
export class Child extends Parent {
public prop2: string;
constructor(prop1: string, prop2: string) {
super(prop1);
this.prop2 = prop2;
}
override setup(): void {
console.log(this.prop2);
}
}
正如你所看到的,何时被调用,还没有被定义,所以它是。setup()
this.prop2
undefined
我建议你在通话结束后从班级内部打电话,而不是上面的电话,就像这样:setup()
Child
super()
export abstract class Parent {
constructor(protected readonly parentProp: string) { }
abstract setup(): void
}
export class Child extends Parent {
constructor(prop1: string, public prop2: string) {
super(prop1);
if (autoSetup) this.setup();
}
override setup(): void {
console.log(this.prop2);
}
}
new Child('prop1', 'prop2') // Prints "prop2"
这样,将在 的上下文中初始化后运行,因此将被正确定义。setup()
this
Child
this.prop2
或者,如果您确实需要从 中调用,则可以将参数作为参数传递给构造函数,尽管在我看来这看起来更糟:setup()
Parent
prop2
Parent
export abstract class Parent {
constructor(protected readonly parentProp: string, prop2: string) {
if (autoSetup) this.setup(prop2);
}
abstract setup(prop2: string): void
}
export class Child extends Parent {
constructor(prop1: string, public prop2: string) {
super(prop1, prop2);
this.prop2 = prop2;
}
override setup(prop2: string): void {
console.log(prop2);
}
}
new Child('prop1', 'prop2') // Prints "prop2"
评论
autoSetup
true
prop2
prop3
super
this
this.prop2