我有大约50个不同的类扩展BaseClass。有些在内部初始化,有些在内部初始化。所有实例都有一个唯一的id
属性
目标是知道哪个实例启动了哪个实例(通过对Caller.id的一些引用),并在BaseClass的上下文中为此编写代码。
所有初始化都遵循这种格式,它们在init之后调用'method':new ChildClass().method()
new ChildClass().method().bind(this)不是一个很好的选择,不幸的是
代码如下所示
class ChildClass extends BaseClass {
id = 'ChildId';
}
class ParentClass extends BaseClass{
id = 'ParentId';
parentMethod() {
new ChildClass().method()
}
}
abstract class BaseClass {
protected id;
private callerId;
constructor() {
// either here we set callerId from params
}
method() {
// or here we set callerId from params
}
}
我想避免在我的ParentClass中做以下事情。
const callerId = this.id;
new ChildClass(callerId).method()
或
const callerId = this.id;
new ChildClass().method(callerId)
但是我仍然需要我的BaseClass来抓取callerId
不确定这是否可能:)
如果我理解正确的话,您需要保留一个指向实例化该类的类的引用/链接。可能有不同的解决方案。我预见的最简单的方法是替换类上下文中的new
:
type Constructor<T> = { new (...args): T, prototype: T };
abstract class BaseClass {
protected id;
private callerId;
_new<T extends BaseClass>(clazz: Constructor<T>, ...args): T {
const i = new clazz(...args);
i.callerId = this.id;
return i;
}
method() {
}
}
class ChildClass extends BaseClass {
id = 'ChildId';
}
class ParentClass extends BaseClass{
id = 'ParentId';
parentMethod() {
this._new(ChildClass).method();
}
}
我不完全了解最新JS/TS版本可以完成的所有巫毒。据我所知,如果您希望将代码保持为:
的形式,您就无能为力了。new ChildClass().method();
为什么?那么,瑞士刀添加魔法到你的JS代码是Monkey patching
。但是通过设计,当您使用new
时,将创建一个新对象并将其作为当前this
上下文传递给构造函数。这种行为使我们失去了对原始引用的跟踪,但无法更改,因此没有解决方案。
我在说谎
在JS中,你可以随心所欲地改变规则(这就是为什么许多开发人员讨厌它的原因)。当然不是我)。我想到的一个非常不寻常的解决方案是受到Vue.js值绑定核心的启发,它的工作原理如下:
let __BASE_CLASS_ACT_ID = undefined; // (1)
abstract class BaseClass {
protected id;
private callerId;
constructor() {
// Catch my caller according to the current context
this.callerId = __BASE_CLASS_ACT_ID;
// Patch all my methods
for (const name of Object.getOwnPropertyNames(this)) {
if (typeof this[name] === 'function') {
const _old = this[name]; // (3)
this[name] = (...args) => { // (2)
__BASE_CLASS_ACT_ID = this.id;
_old.apply(this, args);
__BASE_CLASS_ACT_ID = undefined;
}
}
}
}
}
这里我们利用了JS是单线程语言的事实,因此任何函数都可以一次调用。我们保留了一个全局变量(1)
,它在补丁函数(2)
中更新,在调用原始函数(3)
之前设置它,然后重置它。这应该允许你让它工作:
parentMethod() {
new ChildClass().method();
}
注意这在构造函数中不起作用:
constructor() {
super();
new ChildClass().method(); // not working
}
也不能用于async函数:
parentMethod() {
doSome().then(x => {
new ChildClass().method(); // not working
})
}
这个例子可以改进和加强,但它将永远是一个hack,使用一个简单的_new
直接解决你的问题。