单例和实例化实例的模块有什么区别?

What is the difference between a singleton and a module that instantiates an instance?

我想在我的代码中添加一个指标 class。

有什么区别
class MyMetrics {
  constructor () {
    if (!Singleton.instance) {
      Singleton.instance = new Metrics()
    }
    return Singleton.instance
  }
}

const metrics = new MyMetrics()

export const metrics = new Metrics()

导入的每个模块 metrics 不会使用相同的 Metrics 实例吗?

它们在我的使用中功能相同吗?推荐哪个?

Wouldn't each module that imported metrics be using the same Metrics instance?

是的,他们会的。

Are they functionally the same for my usage?

只要 A) 您没有在模块中创建其他实例并且 B) 您没有导出 Metrics,是的,差不多。但要记住的一件事是,任何可以访问您的 metrics 导入的代码都可以访问您的 Metrics 构造函数,间接地通过 constructor 属性 metrics 继承来自 Metrics.prototype:

import { metrics } from "./your-module.js";
const newMetrics = new metrics.constructor();

您可能认为使用单例可以避免这种情况,但这很容易被推翻,因为 instance 是 public:

import { metrics } from "./your-module.js";
const Metrics = metrics.constructor;
Metrics.instance = null;
const newMetrics = new Metrics();

因此您可能希望将其设为私有(使用静态私有 属性,或者仅使用 metrics 本身来检查您是否已创建它)。

Which would be recommended?

这是一个见仁见智的问题。但是您甚至可能根本不需要 class。你可以:

  • 直接做一个对象就可以了,不用构造函数
  • 导出函数并关闭模块私有变量。

例如,考虑这个(相当愚蠢)class:

// stuff.js
class Stuff {
    #items = new Map();
    constructor() {
        // ...pretend there's singleton logic here...
    }
    put(key, value) {
        this.#items.set(key, value);
    }
    get(key) {
        return this.#items.get(key);
    }
}
export const stuff = new Stuff();

您的使用方式是:

import { stuff } from "./stuff.js";

stuff.put("this", "that");
stuff.get("this"); // "that"

您可以完全摆脱 class:

// stuff.js
const items = new Map();
export const stuff = {
    put(key, value) {
        items.set(key, value);
    },
    get(key) {
        return items.get(key);
    }
};

用法相同:

import { stuff } from "./stuff.js";

stuff.put("this", "that");
stuff.get("this"); // "that"

或者您可以导出 putget:

// stuff.js
const items = new Map();
export const put = (key, value) => {
    items.set(key, value);
};
export const get = (key) => items.get(key);

那么用法是:

import { get, put } from "./stuff.js";

put("this", "that");
get("this"); // "that"

或者如果这些名称可能与其他名称冲突:

import { get as getStuff, put as putStuff } from "./stuff.js";

putStuff("this", "that");
getStuff("this"); // "that"

所以你有很多选择。如果您需要构造具有共享特征的多个对象,构造函数(包括使用 class 语法创建的函数)很有用,但如果您不这样做(并且您没有使用单例),只需编写对象直接或(使用模块)直接导出它可以做的事情对你来说可能是不错的选择。