DEV Community

Cover image for The Module System in Javascript
Srishti Prasad
Srishti Prasad

Posted on • Edited on

The Module System in Javascript

In the ever-evolving landscape of JavaScript development, mastering the module system is crucial for creating scalable, maintainable, and efficient code, transforming how developers structure and share their projects across the modern web.

Necessity of module system

  1. Code Organization: Having a way to split the codebase into multiple files, modules help in organizing code into manageable, logical units. This makes the codebase easier to understand and maintain.
  2. Reusability: Modules allow you to reuse code across different parts of an application or even across different projects.
  3. Encapsulation: Modules encapsulate code, which means they expose only what is necessary and hide the internal details. This prevents unintended interactions and reduces bugs.
  4. Dependency Management: A good module system should make it easy for module developer to build on top of existing modules. Modules help in managing dependencies between different parts of the application, making it clear what functionality depends on what.

Module - is actual unit of software that encapsulates related functionality

Module System - on the other hand, is a set of rules, conventions, and mechanisms that enable the creation, importation, and use of modules within a programming language or environment. It defines how modules are written, organized, and accessed within an application example : CommonJS and ECMAScript Modules

CommonJs Module system

CommonJs is the first module system in Nodejs.CommonJs respects the CommonJs specification, with addition of some custom extensions.

  • it uses the require() function to import modules.

  • module.exports or exports are special variables that can be used to export public functionality from the current module.

The require() function in CommonJS modules is synchronous. When you use require() to import a module, Node.js will load and execute the module synchronously, blocking the execution of the code until the required module is fully loaded and its exports are available.

module.exports vs exports

Using module.exports

is an object provided by Node.js for defining what a module exports. When you assign a value to module.exports, you are replacing the entire exports object with whatever value you provide.

function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}

module.exports = {
    add,
    subtract
};
Enter fullscreen mode Exit fullscreen mode

Using exports

exports is a shorthand reference to module.exports. Initially, exports is set to reference the same object as module.exports. You can use the exports object to add properties and methods to the exports object.

exports.add = function(a, b) {
    return a + b;
};

exports.subtract = function(a, b) {
    return a - b;
};
Enter fullscreen mode Exit fullscreen mode

Module defination patterns

In Node.js, there are several popular patterns for defining and exporting modules. These include:

  • named exports,
  • exporting functions,
  • exporting classes,
  • exporting instances, and
  • monkey patching

Named exports

Uses module.exports or exports to export multiple values from a module.

function verbose(name) {
    console.log("verbose");
}
const logger = "Goodbye!";

module.exports = {
    verbose,
    logger
};
Enter fullscreen mode Exit fullscreen mode

Exporting functions

function verbose(){
console.log("verbose")
}
module.exports = verbose
Enter fullscreen mode Exit fullscreen mode

Same pattern of export is followed in exporting class or an instance of class
Creating a class or an instance of class and exporting it using module.export

Exporting classes

Modules can also export a class, which can then be instantiated by the importing code.

class Greeter {
    constructor(greeting) {
        this.greeting = greeting;
    }
    greet(name) {
        return `${this.greeting}, ${name}`;
    }
}

module.exports = Greeter;

//Usage
const Greeter = require('./myClassModule');
const greeter = new Greeter('Hello');
console.log(greeter.greet('Charlie')); 
Enter fullscreen mode Exit fullscreen mode

Exporting Instances

Instead of exporting a class, you can export an instance of the class, ensuring there is a single shared instance.

class Greeter {
    constructor() {
        this.greeting = 'Hello';
    }
    greet(name) {
        return `${this.greeting}, ${name}`;
    }
}

const greeterInstance = new Greeter();
module.exports = greeterInstance;

//Usage
const greeter = require('./myInstanceModule');
console.log(greeter.greet('Dana'));
Enter fullscreen mode Exit fullscreen mode

Reference Book : NodeJs Design pattern by Mario Casciaro

This blog was mainly about module system and CommonJs module .
It is extremely important to understand ES Module ,in the next blog I'll cover specifically ES Module and compare both module. ES Module
Monkey patching is an important topic to learn will post separate blog on this.
Let me know if you have any query,will try me best to address them all to the best of my knowledge.

Top comments (7)

Collapse
 
efpage profile image
Eckehard

Finally, you are only exporting identifiers about which nothing is known. So, an export can be anything, a class, a function or a variable and it is up to you to care about. This is bad if you develop modules for larger teams. As you are not able to describe precisely, what you are exporting, the system cannot care about the right usage. All the effort you do to export and import identifiers manually is worth nothing.

This is a reason why many libraries simply export only one object to which all exports are attached as properties.

Collapse
 
srishtikprasad profile image
Srishti Prasad

@efpage , Thanks for your insightful comment. You raise a valid point about the potential issues with exporting individual identifiers in larger projects. Ensuring the correct usage and maintaining clarity can indeed become challenging in such cases.

Exporting a single object that encapsulates all exports is a good practice to improve maintainability and readability. This method allows developers to see all available exports in one place, reducing the risk of incorrect usage and making the module's API more predictable.

I appreciate you bringing this up as it adds valuable perspective to the discussion on module exports. While exporting individual identifiers can offer flexibility, especially with modern bundlers supporting tree-shaking, the approach of using a single object can indeed provide a clearer and more manageable API, particularly in larger codebases.

Collapse
 
efpage profile image
Eckehard

To be honest, IÂŽm not a big fan of the Javascript module system. Exporting individual indentifiers can drive you nuts if as you have to reflect any change of a library in every module that uses the lib. Putting everything into a single object is convenient, but will break your bundlers ability for tree shaking. As far as I could figure out, bundlers like webpack and rollup work only on identifiers that are exported individually. If you combine them in an object, they will always use the whole module.

Thread Thread
 
srishtikprasad profile image
Srishti Prasad • Edited

nice insight @efpage !!

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

Warning for newbies: Avoid CommonJS. Adopt the norm. Adopt the native ES modules. It is good to know about CommonJS, but avoid it like the plague as much as possible.

Collapse
 
srishtikprasad profile image
Srishti Prasad

@webjose , Thanks for your comment and sharing your invaluable insight to our readers. You make an excellent point about the importance of adopting modern standards like ES modules over CommonJS. You make a valid point about the importance of adopting modern standards.

I completely agree with you, the trend is indeed shifting towards ES modules. Many modern JavaScript environments and bundlers now support ES modules, making it easier to adopt them in new projects.

For those starting with JavaScript and Node.js, it’s beneficial to become familiar with ES modules and use them wherever possible. However, understanding CommonJS is still valuable, especially when working with legacy code or certain npm packages that haven’t transitioned to ES modules yet.
Do checkout my ES module blog post and share your views :)
ES Module

Collapse
 
mkvillalobos profile image
Manrike Villalobos BĂĄez

Absolutely agree!! Avoid CommonJS! It served a purpose quite well a long time ago, but now, is no longer necessary because native ES modules are the standard... are the norm.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.