【问题标题】:TypeScript class function as function VS variable which has better performaceTypeScript 类函数作为函数 VS 变量,具有更好的性能
【发布时间】:2021-07-27 06:38:17
【问题描述】:

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

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

这是我想到的不同方式:

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

我尝试使用JSBen 来测量差异,但结果似乎是随机的。

【问题讨论】:

    标签: javascript typescript function class tree-shaking


    【解决方案1】:

    如果您担心在这个级别上优化性能,那么拥有一个只有静态方法的类会引入一些完全不必要的开销。类是为实例化而设计的,如果您不使用该功能,您将浪费一些计算资源来提供这些功能。

    当我运行您的示例(MacOS、Chrome 90.0.4430.93)时,我得到了这个:

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


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

    const obj = {
      functionE(v, maxDeep, curDeep = 0) {
        //...
      },
    
      functionF: (v, maxDeep, curDeep = 0) => {
        //...
      }
    };
    

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


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

    function rawFunctionStatementG(v, maxDeep, curDeep = 0) {
      //...
    }
    
    const rawFunctionVarH = (v, maxDeep, curDeep = 0) => {
      //...
    };
    

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

    Updated jsben.ch


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

    如果你这样做:

    // util-fns.ts
    export function myFuncA() { /* ... */ }
    export function myFuncB() { /* ... */ }
    
    // some consumer file
    import { myFuncA } from './util-fns'
    
    

    然后,捆绑器很容易看到 myFuncB 从未被导入或引用,并且可能会被修剪。


    最后...

    “真正的问题是程序员在错误的地方和错误的时间花费了太多时间来担心效率;过早的优化是编程中万恶之源(或至少大部分)。” ——唐纳德·高德纳

    这种级别的性能优化不太可能在广大大多数 JavaScript 应用程序中发挥作用。因此,根据代码库/组织的标准,使用对应用程序最有意义的数据结构。这些事情要重要得多。

    【讨论】:

    • 不错的答案。我还要补充一点,性能更高的导出函数版本更加惯用且更易于维护。静态方法实际上更难推理,因为它们可以在派生类中被隐藏(人们喜欢称其为覆盖,但事实并非如此),这进一步解释了为什么它们要慢得多。
    • 非常感谢。很棒的答案。这对我来说很有意义,现在。基本上,类只有在至少有一个非静态成员时才有意义。我完全错了。
    猜你喜欢
    • 2021-03-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    • 2021-04-29
    • 1970-01-01
    相关资源
    最近更新 更多