我通过 Mixins 使用多重继承(使用替代模式)。有没有办法用这种模式获得类似于“超级”的东西?考虑这里的例子
abstract class Activatable{
private activated: boolean = false;
constructor(){}
public activate(): void {
this.activated = true;
console.log('Activatable activated')
}
}
class SomethingElse{};
interface ThingParent extends Activatable{}
class ThingParent extends SomethingElse{
public activate(): void {
(this as Activatable).activate();
let addSomeValueHere = true;
console.log('Thing parent activated')
}
}
applyMixins(ThingParent, [Activatable]);
class Thing extends ThingParent {
constructor(){
super()
}
public activate(): void {
super.activate();
console.log('Thing activated');
}
}
let thing = new Thing();
thing.activate();
function applyMixins(derivedCtor: any, constructors: any[]) {
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
}
Thing 扩展了 ThingParent,后者通过 mixin 扩展了一个名为 Activatable 的类。当我在 Thing 上调用 activate 时,我也想在 ThingParent 和 Activatable 上调用 activate,但它只在 Activatable 上调用。
如果我更改 ThingParent 上的函数名称,我可以直接调用该函数,但 ThingParent 无法通过 super 访问 Activatable (因为它扩展了 SomethingElse)。然后我必须从 thing.activate() 调用 super.activate() 和 this.thingParentActivate() 所以这是我希望尽可能避免的模式。
还有其他选择吗?
如您所知,JavaScript 类不支持多重继承,因此如果您想获得这种效果,您需要做一些事情来模拟它。 Mixins 是实现此目的的一种方法,但它们并不真正支持方法名称冲突。如果你有冲突的方法名称,那么 mixins 最终会破坏除其中一个之外的所有方法,这不是你想要的。
如果您想在“子类”的实例上调用特定“超类”的方法,您可以直接使用
Function.prototype.call()
并将实例作为 this
参数来执行此操作。您不调用 Activatable::activate()
,而是调用 Activatable.prototype.activate.call(this);
。
让我们尝试一下:
interface ThingParent extends Activatable { }
class ThingParent extends SomethingElse {
public activate(): void {
Activatable.prototype.activate.call(this);
let addSomeValueHere = true;
console.log('Thing parent activated')
}
}
class Thing extends ThingParent {
constructor() {
super()
}
public activate(): void {
ThingParent.prototype.activate.call(this);
console.log('Thing activated');
}
}
let thing = new Thing();
thing.activate();
// [LOG]: "Activatable activated"
// [LOG]: "Thing parent activated"
// [LOG]: "Thing activated"
编译正常并产生您期望的输出。
在更复杂的场景中,您可能决定需要组合使用 mixin 和直接方法
call
。或者其他一些方法。
谢谢@jcalz!
有 Angular 示例如何从 mixin 调用 super.ngOnInit。
type Constructor<T = object> = new (...args: any[]) => T;
export function WithTheMixin<BaseType extends Constructor<object>>(Base: BaseType = (class {} as any)):any {
return class extends Base implements OnInit{
ngOnInit(): void {
// super on Init
if (Base.prototype && Base.prototype.ngOnInit){
Base.prototype.ngOnInit.call(this);
}
console.log('Mixin onInit');
}
}
}
@Component({
template: '',
styles: []
})
export class TheAbstractComponent implements OnInit {
ngOnInit(): void {
console.log('TheAbstractComponent onInit');
}
}
@Component({
selector: 'the-component',
template: '<span>hello!</span>'
})
export class TheComponent extends WithNavMenuData(TheAbstractComponent) {
}