在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;