TypeScript 类函数作为函数 VS 变量,性能更好

G-w*_*ave 3 javascript class function typescript tree-shaking

使用定义函数的变量还是首先使用函数更好?此外,tree-shaking 有区别吗?

我有很多计算(静态)密集型帮助程序类,并且想知道最好的(内存/速度)是什么。

这是我想到的不同方法:

class MyClass {
  readonly functionA = (v: string | number, maxDeep: number, curDeep: number = 0): string => {
      if (curDeep < maxDeep) {
          return this.functionA(v, maxDeep, curDeep + 1);
      } else {
          return "function A" + v;
      }
  }

  static functionB(v: string | number, maxDeep: number, curDeep: number = 0): string {
      if (curDeep < maxDeep) {
          return MyClass.functionB(v, maxDeep, curDeep + 1);
      } else {
          return "function B" + v;
      }
  }

  functionC(v: string | number, maxDeep: number, curDeep: number = 0): string {
      if (curDeep < maxDeep) {
          return this.functionC(v, maxDeep, curDeep + 1);
      } else {
          return "function C" + v;
      }
  }

  static readonly functionD = (v: string | number, maxDeep: number, curDeep: number = 0): string => {
      if (curDeep < maxDeep) {
          return MyClass.functionD(v, maxDeep, curDeep + 1);
      } else {
          return "function D" + v;
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用JSBen来测量差异,但结果似乎是随机的。 在此输入图像描述

Ale*_*yne 6

如果您非常关心在此级别上优化的性能,那么拥有一个仅具有静态方法的类会带来一些完全不必要的开销。类被设计为可以实例化,如果您不使用该功能,那么您就会浪费一些计算资源来使用这些功能。

\n

当我运行你的示例(MacOS、Chrome 90.0.4430.93)时,我得到以下信息:

\n

在此输入图像描述

\n

很明显,静态方法具有很大的性能成本。实例方法保证快速。我希望我能告诉你原因,但我确信这与类被设计为实例化的事实有关。

\n
\n

比这简单得多,是一个简单的对象。让我们添加这两个测试:

\n
const obj = {\n  functionE(v, maxDeep, curDeep = 0) {\n    //...\n  },\n\n  functionF: (v, maxDeep, curDeep = 0) => {\n    //...\n  }\n};\n
Run Code Online (Sandbox Code Playgroud)\n

在此输入图像描述

\n

它们的运行速度与实例方法一样快。这是一种更有意义的模式。没有类,因为没有实例化。

\n
\n

但还有一个更简单的选择:

\n
function rawFunctionStatementG(v, maxDeep, curDeep = 0) {\n  //...\n}\n\nconst rawFunctionVarH = (v, maxDeep, curDeep = 0) => {\n  //...\n};\n
Run Code Online (Sandbox Code Playgroud)\n

在此输入图像描述

\n

就性能而言,我们这里有一个赢家。我相当确定这是因为您永远不必查找对象的属性。您可以直接引用该函数,并且可以执行它而无需询问任何其他对象首先在哪里找到它。

\n

更新了 jsben.ch

\n
\n

就摇树而言,这种形式是迄今为止最好的。对于捆绑程序来说,管理整个导出的值是最简单的,而不是尝试对单个对象进行切片和切块。

\n

如果你做类似的事情:

\n
// util-fns.ts\nexport function myFuncA() { /* ... */ }\nexport function myFuncB() { /* ... */ }\n\n// some consumer file\nimport { myFuncA } from \'./util-fns\'\n\n
Run Code Online (Sandbox Code Playgroud)\n

然后,捆绑器非常容易看到myFuncB从未导入或引用的内容,并且可能会被删除。

\n
\n

最后...

\n
\n

\xe2\x80\x9c 真正的问题是程序员在错误的地方和错误的时间花费了太多的时间来担心效率;过早的优化是编程中万恶之源(或者至少是大部分罪恶的根源)。\xe2\x80\x9d \xe2\x80\x93 Donald Knuth

\n
\n

这种级别的性能优化在广泛的情况下不太可能产生影响。因此,请根据代码库/组织的标准,使用对应用程序最有意义的数据结构。这些事情更重要。

\n