Understanding the Statefulness of JS Modules

Mohsen Mahabadi
3 min readOct 3, 2023

--

Statefulness refers to the ability of a system or component to maintain information (or “state”) over time and across interactions. In the context of software, a stateful system remembers data about past interactions and uses that data to influence future operations.

When discussing module systems in JavaScript, such as CommonJS (CJS) and ES6 modules, statefulness pertains to how modules retain and manage internal state across imports and interactions.

Statefulness in CommonJS (CJS)

Singleton Nature:

In CommonJS, once a module is required for the first time, it’s cached. Any subsequent require calls for the same module will retrieve the cached version. This ensures that all parts of an application share the same instance of the module, making it stateful.

counter.js
app.js
another.js

Let’s break down the example:

  1. In app.js, when you first require('./counter'), the counter.js module is executed, and its exports (increment, count, and getCount) are determined and cached.
  2. You then call the increment function, which modifies the internal count variable of the counter.js module.
  3. When you require('./another') in app.js, the another.js file is executed.
  4. Inside another.js, when you require('./counter') again, Node.js doesn't re-execute the counter.js module. Instead, it retrieves the cached exports from the first require call in app.js. This is the singleton behavior in action.
  5. As a result, the getCount function in another.js reflects the updated value of the count variable, which was modified by the increment function call in app.js.

Regardless of where or how many times a module is required, it's initialized only once, and its state is retained across the entire application. This behavior allows different parts of an application to interact with and share the same module instance and its state.

No Live Bindings:

When you require a module in CommonJS, you get a snapshot of its exports at that time. If the module’s internal state changes after the initial import, other modules won’t see those changes unless they interact with the module’s exported functions or objects.

Statefulness in ES6 Modules

Singleton Nature:

Similar to CommonJS, ES6 modules are also singletons. Regardless of how many times you import a module, you always get the same instance, ensuring a consistent state across imports.

Live Bindings:

ES6 modules introduce the concept of live bindings. When you import a value from an ES6 module, you get a live reference to it. If the exported value changes in the module, all importing modules will see the updated value.

In the ES6 example, the count variable inapp.mjs reflects the updated value after theincrement function is called, showcasing the live binding feature.

In Summary

Both CommonJS and ES6 modules are stateful, meaning they can maintain and share state across different parts of an application. However, the way they handle and propagate state differs:

  • CommonJS captures a snapshot of the module’s exports at the time of import, and there are no live bindings.
  • ES6 modules provide live references to their exports, allowing real-time updates to be seen across all importing modules.

Understanding the stateful nature of these module systems is crucial for designing and managing applications, especially in environments that might use both module systems, like Node.js.

--

--