TypeScript class function as function VS variable which has better performace

1.2k Views Asked by At

Is it better using variables which define a function or using a function in the first place? Furthermore, is there a difference for tree-shaking?

I have a lot of calculation (static) intensive helper classes and was wondering what the best (memory/speed) is.

Here the different ways I have in mind:

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

I tried using JSBen for measuring a difference but the results seem to be random. enter image description here

1

There are 1 best solutions below

2
On

If you are that concerned about performance to optimize at this level, then having a class that only has static methods introduces some totally unnecessary overhead. Classes are designed to be instantiated, and if you don't use that feature you are wasting some computational resources to have those features available.

When I run your examples (MacOS, Chrome 90.0.4430.93) I get this:

enter image description here

What's clear is that static methods have a large performance costs. Where instance methods are guaranteed to be fast. I wish I could tell you why, but I'm sure it has something to do with the fact that classes are design to be instantiated.


Much simpler than that, is a simple object. Let's add these two tests:

const obj = {
  functionE(v, maxDeep, curDeep = 0) {
    //...
  },

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

enter image description here

Those run just about as fast as the instance methods. And it's a pattern that makes more sense. There's no classes because there is no instantiation.


But there's an even simpler alternative:

function rawFunctionStatementG(v, maxDeep, curDeep = 0) {
  //...
}

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

enter image description here

Performance wise, we have a winner here. I'm fairly sure this is because you never have to look up a property on an object. You have a reference to the function directly, and can execute it without asking any other object where to find it first.

Updated jsben.ch


And as far as tree shaking, this form is by far the best. It's easiest for bundlers to manage whole exported values, rather than trying to slice and dice individual objects.

If you do something like:

// util-fns.ts
export function myFuncA() { /* ... */ }
export function myFuncB() { /* ... */ }

// some consumer file
import { myFuncA } from './util-fns'

Then it's super easy for a bundler to see that myFuncB is never imported or referenced, and could be potentially pruned.


Lastly...

“The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.” – Donald Knuth

It's very unlikely for performance optimizations of this level to matter in the vast majority of javascript applications. So use the data structures that make the most sense for the application, and according to the standards of the codebase/organization. Those things matter so much more.