I want to add a metrics class to my code. What is the difference between
class MyMetrics {
constructor () {
if (!Singleton.instance) {
Singleton.instance = new Metrics()
}
return Singleton.instance
}
}
const metrics = new MyMetrics()
and
export const metrics = new Metrics()
Wouldn't each module that imported metrics
be using the same Metrics instance?
Are they functionally the same for my usage? Which would be recommended?
Wouldn't each module that imported
metrics
be using the same Metrics instance?
Yes, they would.
Are they functionally the same for my usage?
As long as A) you aren't creating other instances within the module and B) you aren't exporting Metrics
, yes, almost. But one thing to remember is that any code that has access to your metrics
import has access to your Metrics
constructor, indirectly via the constructor
property metrics
inherits from Metrics.prototype
:
import { metrics } from "./your-module.js";
const newMetrics = new metrics.constructor();
You might think that with the singleton, you've avoided that, but that's easily defeated as instance
is public:
import { metrics } from "./your-module.js";
const Metrics = metrics.constructor;
Metrics.instance = null;
const newMetrics = new Metrics();
So you might want to make it private (either using a static private property, or just using metrics
itself to check if you've created it).
Which would be recommended?
That's a matter of opinion. But you might not even need a class at all. You could:
For instance, consider this (fairly silly) 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();
The way you use that is:
import { stuff } from "./stuff.js";
stuff.put("this", "that");
stuff.get("this"); // "that"
You could just get rid of the class entirely:
// stuff.js
const items = new Map();
export const stuff = {
put(key, value) {
items.set(key, value);
},
get(key) {
return items.get(key);
}
};
Usage would be the same:
import { stuff } from "./stuff.js";
stuff.put("this", "that");
stuff.get("this"); // "that"
Or you could just export put
and get
:
// stuff.js
const items = new Map();
export const put = (key, value) => {
items.set(key, value);
};
export const get = (key) => items.get(key);
Then usage is:
import { get, put } from "./stuff.js";
put("this", "that");
get("this"); // "that"
Or if those names may conflict with other things:
import { get as getStuff, put as putStuff } from "./stuff.js";
putStuff("this", "that");
getStuff("this"); // "that"
So you have lots of options to choose from. Constructor functions (including ones created with class
syntax) are useful if you need to construct multiple objects with shared characteristics, but if you aren't doing that (and you aren't with a singleton), just writing the object directly or (with modules) exporting the things it can do directly may be good options for you.