关于 JS class 助手的最佳实践(放在 class 中或 class 之外)?

Best practice regarding helpers of JS class (place in or out of class)?

我最近遇到了一些显示大量数据的 mapbox 应用程序的性能和内存管理问题(浏览器会在大约 20 小时后因累积过多垃圾而崩溃 - public,高度可见,完全设计从来不需要自动化机器和更新)。虽然事实证明问题与 mapbox 处理渲染和动画的方式密切相关,并且或多或少已修复,但它确实让我想知道是否有任何地方可以改进应用程序的性能和内存管理。

这引发了以下问题:

我有一个单例 class(一项服务),注入了多个组件。

在这个 class 中,我有多个辅助方法,它们可能在单例外部,但从应用程序的逻辑来看,它们属于服务(它们目前未在其他任何地方使用)。不可否认,如果应用程序逻辑发生变化,它们可能会用于其他组件。

在 class 内部或外部声明 helpers 更好吗? 如果它取决于某些因素,那些因素是什么?

我能做到:

const helperA = () => stuff

export class MyClass {
  // ... methods/constructor where I use helperA()
}

显然,助手可以放在另一个文件中并导入,但这无关紧要。

或者我可以

export class MyClass {
  private helperA = () => stuff;
  // ... methods/constructor where I use this.helperA()
}

我很想将 helperA 放在 class 之外。这可能是一个更好的原则,因为万一应用程序逻辑发生变化并且我将在其他地方需要它,helperA 已准备好导出,而无需实例化 class.

我意识到这也算是一种设计偏好吧
但是,我对技术方面更感兴趣。在我决定放置助手之前,我想知道以一种或另一种方式进行操作是否有任何优势或劣势。

它们在这里示意性地呈现,但可能有大量的助手,其中一些可能用于大量数据集合,性能是这里的一个重要方面,内存管理(垃圾收集)也是如此(即: 当 class 实例被删除时,外部定义的常量是否被垃圾收集?我的猜测是它们不是,因为在 class 的另一个成员被实例化的情况下需要它们。但是另一个疯狂的猜测是它无关紧要,因为 class 是单例)。

以下是我在 Chrome 中 运行 的一些测试(我最感兴趣):
我已经 运行 每个测试至少 3 次并且结果时间是一致的。

a) helpers inside, 在 1e3 class instances 上调用 2 × 1e7 次: 132170.80500000156 ( > 2 分钟)

const t0 = performance.now();

class MyClass {
  value = 0;
  up = l => l + 1;
  down = l => l - 1;

  constructor() {
    for (let i = 0; i < 1e7; i++) {
      this.up(this.value);
    }
    for (let i = 0; i < 1e7; i++) {
      this.down(this.value);
    }
  }
}
let a;
for (let i = 0; i < 1e3; i++) {
  a = new MyClass();
}
console.log({
  duration: performance.now() - t0
});

b) helpers outside, called 2 × 1e7 times for 1e3 class instances: 5935.109999991255 ( < 6 秒)

const t0 = performance.now();
const up = l => l + 1;
const down = l => l - 1;
class MyClass {
  value = 0;

  constructor() {
    for (let i = 0; i < 1e7; i++) {
      up(this.value);
    }
    for (let i = 0; i < 1e7; i++) {
      down(this.value);
    }
  }
}
let a;
for (let i = 0; i < 1e3; i++) {
  a = new MyClass();
}
console.log({
  duration: performance.now() - t0
});

c) helpers inside, called 2 × 1e10 times on 1 class instance: 21271.935000026133 ( > 20 秒)

const t0 = performance.now();

class MyClass {
  value = 0;
  up = l => l + 1;
  down = l => l - 1;

  constructor() {
    for (let i = 0; i < 1e10; i++) {
      this.up(this.value);
    }
    for (let i = 0; i < 1e10; i++) {
      this.down(this.value);
    }
  }
}
let a = new MyClass();

console.log({
  duration: performance.now() - t0
});

d) helpers outside, called 2 × 1e10 times for 1 class instance: 21521.570000011707 ( > 20 秒)

const t0 = performance.now();
const up = l => l + 1;
const down = l => l - 1;
class MyClass {
  value = 0;

  constructor() {
    for (let i = 0; i < 1e10; i++) {
      up(this.value);
    }
    for (let i = 0; i < 1e10; i++) {
      down(this.value);
    }
  }
}
let a = new MyClass();
console.log({
  duration: performance.now() - t0
});

在每个测试中,助手被调用的次数相同。

性能 而言,我的结论是,将助手放在单例中的位置无关紧要。当处理多个实例时,它变得相关,最好将它们放在外面。


内存分配而言,我的结果尚无定论。 None 的方法似乎分配了更多的内存或随着时间的推移积累了大量的垃圾。