A module is a stand-alone file that encapsulates code to implement certain functionality and promote reusability and organization.
Here you will cover the module systems used in JavaScript applications, including the module pattern, the CommonJS module system used in most Node.js applications, and the ES6 Module system.
The Module Pattern
Before the introduction of native JavaScript modules, the module design pattern was used as a module system to scope variables and functions to a single file.
This was implemented using immediately invoked function expressions, popularly known as IIFEs. An IIFE is an un-reusable function that runs as soon as it is created.
Here’s the basic structure of an IIFE:
The code block above describes IIFEs used in three different contexts.
IIFEs were used because variables declared inside a function are scoped to the function, making them only accessible inside the function, and because functions allow you to return data (making them publicly accessible).
For example:
The code block above is an example of how modules were created before the introduction of native JavaScript modules.
The code block above contains an IIFE. The IIFE contains a function that it makes accessible by returning it. All the variables declared in the IIFE are protected from the global scope. Thus, the method (sayName) is only accessible through the public function, callSayName.
Notice that the IIFE is saved to a variable, foo. This is because, without a variable pointing to its location in memory, the variables will be inaccessible after the script runs. This pattern is possible due to JavaScript closures.
The CommonJS Module System
The CommonJS module system is a module format defined by the CommonJS group to solve JavaScript scope issues by executing each module in its namespace.
The CommonJS module system works by forcing modules to explicitly export variables they want to expose to other modules.
This module system was created for server-side JavaScript (Node.js) and, as such, is not supported by default in browsers.
To implement CommonJS modules in your project, you have to first initialize NPM in your application by running:
Variables exported following the CommonJS module system can be imported like so:
Modules are imported in CommonJS using the require statement, which reads a JavaScript file, executes the read file, and returns the exports object. The exports object contains all the available exports in the module.
You can export a variable following the CommonJS module system using either named exports or default exports.
Named Exports
Named exports are exports identified by the names they were assigned. Named exports allow multiple exports per module, unlike default exports.
For example:
In the code block above, you are exporting two named functions (myExport and anotherExport) by attaching them to the exports object.
Similarly, you can export the functions like so:
In the code block above, you set the exports object to the named functions. You can only assign the exports object to a new object through the module object.
Your code would throw an error if you attempted to do it this way:
There are two ways you can import named exports:
- Import all the exports as a single object and access them separately using the dot notation.
For example:
- De-structure the exports from the exports object.
For example:
One thing is common in all the methods of importing, they must be imported using the same names they were exported with.
Default Exports
A default export is an export identified by any name of your choice. You can only have one default export per module.
For example:
In the code block above, you are exporting a class (Foo) by reassigning the exports object to it.
Importing default exports is similar to importing named exports, except that you can use any name of your choice to import them.
For example:
In the code block above, the default export was named Bar, although you can use any name of your choice.
The ES6 Module System
ECMAScript Harmony module system, popularly known as ES6 modules, is the official JavaScript module system.
ES6 modules are supported by browsers and servers, although you require a bit of configuration before using them.
In browsers, you have to specify the type as module in the script import tag.
Like so:
In Node.js, you have to set type to module in your package.json file.
Like so:
You can also export variables using the ES6 module system using either named exports or default exports.
Named Exports
Similar to named imports in CommonJS modules, they are identified by the names they were assigned and allow multiple exports per module.
For example:
In the ES6 module system, named exports are exported by prefixing the variable with the export keyword.
Named exports can be imported into another module in ES6 in the same ways as CommonJS:
De-structuring the required exports from the exports object. Importing all the exports as a single object and accessing them separately using the dot notation.
Here’s an example of de-structuring:
Here’s an example of importing the whole object:
In the code block above, the asterisk (*) means “all”. The as keyword assigns the exports object to the string that follows it, in this case, foo.
Default Exports
Similar to default exports in CommonJS, they are identified by any name of your choice, and you can only have one default export per module.
For example:
Default exports are created by adding the default keyword after the export keyword, followed by the name of the export.
Importing default exports is similar to importing named exports, except that you can use any name of your choice to import them.
For example:
Mixed Exports
The ES6 module standard allows you to have both default exports and named exports in one module, unlike CommonJS.
For example:
Importance of Modules
Dividing your code into modules not only makes them easier to read but it makes it more reusable and also maintainable. Modules in JavaScript also make your code less error-prone, as all modules are executed in strict mode by default.