在TypeScript中每次调用静态class方法时如何调用静态方法?
How to call a static method every time a static class method is called in TypeScript?
我创建了一个单例(抽象)class,它需要先初始化一些数据,然后才能使用大多数方法。这是 class 的示例:
abstract class MyClass {
private static initialize(): void {
// do stuff
}
public static doStuff1(param1: string): string {
MyClass.initialize();
// do stuff
return 'stuff1';
}
public static doStuff2(param2: string[]): string[] {
MyClass.initialize();
// do stuff
return ['stuff2'];
}
}
有没有办法避免在每个方法上添加这些 initialize()
调用?我正在考虑使用装饰器并发现了这个相关问题:
不幸的是,它使用的是遗留装饰器格式,至少这是我得到的错误:
Syntax error - Support for the experimental syntax 'decorators-legacy' isn't currently enabled
我试图使用 stage 2 proposal 找到等效的格式,但也许有更好的选择?
(通常不鼓励单身人士,但由于我不知道你的用例,我会假设你有充分的理由使用它们并且不会进入替代方案)。
为了解决您的问题,您可以使用一些包装实用程序来装饰您的函数:
const wrap = <T extends (...args: any[]) => any>(func: T): T =>
<T>((...args: any[]) => {
// Do something before every function here...
return func(...args);
});
然后您可以像这样定义您的函数:
abstract class MyClass {
public static doStuff1 = wrap((param1: string): string => {
// do stuff
return 'stuff1';
});
}
函数 doStuff1
现在保证在执行它自己的代码之前始终执行 wrap
中的代码。
此方法的问题是您无法访问 MyClass
的私有状态或函数,这可能是您需要的。您可以在每次包装它时传递对该方法的引用,但那样做并不比在每个函数的开头调用它好多少。
更好的方法:非静态单例
如果您无论如何都在使用单例,最好以非静态方式实现它,并且可以免费保证一次性初始化的好处:
class MyClass {
private static instance?: MyClass;
static getInstance = (): MyClass =>
(MyClass.instance = MyClass.instance ?? new MyClass());
private constructor() {
// Do your initialization
console.log("initializing");
}
doStuff1() {
// do stuff
console.log("doing stuff 1");
}
doStuff2() {
// do stuff
console.log("doing stuff 2");
}
}
MyClass.getInstance().doStuff1();
// Output:
// initializing
// doing stuff 1
MyClass.getInstance().doStuff1();
MyClass.getInstance().doStuff2();
// Output:
// doing stuff 1
// doing stuff 2
请注意上面的输出,初始化只调用一次,这(根据您的问题判断)似乎是所需的行为。
我同意 soimons 的回答,因为它鼓励更简洁的代码。然而,您可以使用代理对象来包装您的抽象 class 以便您的调用感觉就像您在使用静态单例。
我不确定这是否有副作用,但它似乎可以满足您的需求。
_MyClass.ts
abstract class _MyClass {
private static count:number = 0;
private static initialize(): void {
console.log('initialized'+_MyClass.count++);
}
private static doStuff1(param1: string): string {
return 'stuff1';
}
private static doStuff2(param2: string[]): string[] {
return ['stuff2'];
}
public static handler2 = {
get: function(target:any, prop:Function) {
_MyClass.initialize();
return Reflect.get(target,prop);
}
};
}
const MyClass = new Proxy(_MyClass, _MyClass.handler2);
export {MyClass};
index.ts
import { MyClass } from "./_MyClass"
alert(MyClass.doStuff1())
以上代码将产生以下结果:
[LOG]: "initialized0"
[LOG]: "stuff1"
[LOG]: "initialized1"
[LOG]: ["stuff2"]
如果这对任何人有帮助,我最终选择了一条更简单的路线,基本上导出了一个非静态 class 的实例。所以它不再是一个真正的单例,这意味着没有什么可以阻止更多实例的初始化,但实际上没有意义(除了测试)这样做。
class MyClass {
private prop1: string[];
private prop2: string;
constructor(): {
// load data into the object
this.prop1 = ['a', 'b'];
this.prop2 = 'c';
}
public getProp1(param: string[]): string[] {
return this.prop1;
}
public getProp2(param: string): string {
return this.prop2;
}
}
const myClass = new MyClass();
export default myClass;
我创建了一个单例(抽象)class,它需要先初始化一些数据,然后才能使用大多数方法。这是 class 的示例:
abstract class MyClass {
private static initialize(): void {
// do stuff
}
public static doStuff1(param1: string): string {
MyClass.initialize();
// do stuff
return 'stuff1';
}
public static doStuff2(param2: string[]): string[] {
MyClass.initialize();
// do stuff
return ['stuff2'];
}
}
有没有办法避免在每个方法上添加这些 initialize()
调用?我正在考虑使用装饰器并发现了这个相关问题:
不幸的是,它使用的是遗留装饰器格式,至少这是我得到的错误:
Syntax error - Support for the experimental syntax 'decorators-legacy' isn't currently enabled
我试图使用 stage 2 proposal 找到等效的格式,但也许有更好的选择?
(通常不鼓励单身人士,但由于我不知道你的用例,我会假设你有充分的理由使用它们并且不会进入替代方案)。
为了解决您的问题,您可以使用一些包装实用程序来装饰您的函数:
const wrap = <T extends (...args: any[]) => any>(func: T): T =>
<T>((...args: any[]) => {
// Do something before every function here...
return func(...args);
});
然后您可以像这样定义您的函数:
abstract class MyClass {
public static doStuff1 = wrap((param1: string): string => {
// do stuff
return 'stuff1';
});
}
函数 doStuff1
现在保证在执行它自己的代码之前始终执行 wrap
中的代码。
此方法的问题是您无法访问 MyClass
的私有状态或函数,这可能是您需要的。您可以在每次包装它时传递对该方法的引用,但那样做并不比在每个函数的开头调用它好多少。
更好的方法:非静态单例
如果您无论如何都在使用单例,最好以非静态方式实现它,并且可以免费保证一次性初始化的好处:
class MyClass {
private static instance?: MyClass;
static getInstance = (): MyClass =>
(MyClass.instance = MyClass.instance ?? new MyClass());
private constructor() {
// Do your initialization
console.log("initializing");
}
doStuff1() {
// do stuff
console.log("doing stuff 1");
}
doStuff2() {
// do stuff
console.log("doing stuff 2");
}
}
MyClass.getInstance().doStuff1();
// Output:
// initializing
// doing stuff 1
MyClass.getInstance().doStuff1();
MyClass.getInstance().doStuff2();
// Output:
// doing stuff 1
// doing stuff 2
请注意上面的输出,初始化只调用一次,这(根据您的问题判断)似乎是所需的行为。
我同意 soimons 的回答,因为它鼓励更简洁的代码。然而,您可以使用代理对象来包装您的抽象 class 以便您的调用感觉就像您在使用静态单例。
我不确定这是否有副作用,但它似乎可以满足您的需求。
_MyClass.ts
abstract class _MyClass {
private static count:number = 0;
private static initialize(): void {
console.log('initialized'+_MyClass.count++);
}
private static doStuff1(param1: string): string {
return 'stuff1';
}
private static doStuff2(param2: string[]): string[] {
return ['stuff2'];
}
public static handler2 = {
get: function(target:any, prop:Function) {
_MyClass.initialize();
return Reflect.get(target,prop);
}
};
}
const MyClass = new Proxy(_MyClass, _MyClass.handler2);
export {MyClass};
index.ts
import { MyClass } from "./_MyClass"
alert(MyClass.doStuff1())
以上代码将产生以下结果:
[LOG]: "initialized0"
[LOG]: "stuff1"
[LOG]: "initialized1"
[LOG]: ["stuff2"]
如果这对任何人有帮助,我最终选择了一条更简单的路线,基本上导出了一个非静态 class 的实例。所以它不再是一个真正的单例,这意味着没有什么可以阻止更多实例的初始化,但实际上没有意义(除了测试)这样做。
class MyClass {
private prop1: string[];
private prop2: string;
constructor(): {
// load data into the object
this.prop1 = ['a', 'b'];
this.prop2 = 'c';
}
public getProp1(param: string[]): string[] {
return this.prop1;
}
public getProp2(param: string): string {
return this.prop2;
}
}
const myClass = new MyClass();
export default myClass;