JavaScript module system as a singleton~ Implementations ~

JavaScript module system as a singleton

There are different types of scope in JavaScript. One of them is the module scope. A variable at the top of the file falls into that scope. In Node, such variable is not part of a function, class, or block scope. In some sense, this is replicated on the client when we bundle our code. Those are not accessible outside the file/module. We have to export them. Another feature is that they are cached. Meaning that no matter how many times we import/require the module, the top-level code executes only once. This allows us to implement the singleton pattern (later in the book, we will discuss that in greater depth).

Let's say that we have a file called registry.js with the following content.

const users = [];
module.exports = {
  register(name) {
    users.push({ name });
  },
  total() {
    return users.length;
  },
};

We defined the users constant at the top level, so it's cached. It is initialized only once. Now, let's create two other files/modules A.js and B.js, that will import registry.js.

// A.js
const R = require("./registry");
R.register("Dave");
module.exports = R;

// B.js
const R = require("./registry");
R.register("Alex");
module.exports = R;

The R constant in both files is the same object. Here's the proof - we will create index.js that will import all three files. It will call the total function from the registry.js module.

const A = require("./A");
const B = require("./B");
const { total } = require("./registry");

console.log(total()); // 2
console.log(A === B); // true

The first log proves that we created the users array only once, and when we call the register function, we are using that same instance. The second log tells us that the exported value in the registry is cached.