When we say an application is modular, we generally mean it's composed of a set of highly decoupled, distinct pieces of functionality stored in modules.
However, unlike other traditional programming languages, for a long time, JavaScript didn't provide developers with the means to import such modules of code in a clean, organized manner.
But, this didn't stop the community and they created impressive work-arounds. Following are the most used module loading patterns used by the developers:
Scripts
are code fragments that browsers run in global scope. They are precursors of modules.CommonJS
modules are a module format that is mainly used on servers (e.g., via Node.js).AMD
is a module format that is mainly used in browsers.ECMAScript modules
are JavaScript’s built-in module format. It supersedes all previous formats.
CommonJS modules#
CommonJS is a project that aims to define a series of specifications to help in the development of server-side JavaScript applications. The CommonJS module proposal specifies a simple API for declaring modules server-side and unlike AMD attempts to cover a broader set of concerns such as IO, filesystem, promises and more.
There are essentially two elements to interact with the module system: require
and exports
require
is a function that can be used to import symbols from another module to the current scopeexports
is a special object: anything put in it will get exported
var lib = require('package/lib');
// some behaviour for our module
function foo(){
lib.log('hello world!');
}
// export (expose) foo to other modules
exports.foo = foo;
Implementations
Since it was made primarily for the server, it is well implemented in NodeJs (Node.js modules have a few features that go beyond CommonJS). For the client there are currently two popular options: webpack and browserify which help bundle these modules.
Asynchronous Module Definition (AMD)#
AMD was born as CommonJS wasn’t suited for the browsers early on. The main difference between AMD and CommonJS lies in its support for asynchronous module loading.
//Calling define with a dependency array and a factory function
define(['dep1', 'dep2'], function (dep1, dep2) {
//Define the module value by returning a value.
return function () {};
});
// Or:
define(function (require) {
var dep1 = require('dep1'),
dep2 = require('dep2');
return function () {};
});
Implementations
Currently the most popular implementations of AMD are require.js and Dojo.
ECMAScript 6 modules#
Fortunately, the ECMA team behind the standardization of JavaScript decided to standardize modules. We finally have a standard to define modules in Javascript which compatible with both synchronous and asynchronous modes of operation.
- On lines of
require
anddefine
, ES6 modules have animport
directive which can be used to bring in modules into namespace. export
helps to explicitly make elements public. A module can have 2 types of exportsnamed exports
which can be several per moduledefault export
primary export of module
Importing examples
// Default exports and named exports
import theDefault, { named1, named2 } from 'src/mylib';
import theDefault from 'src/mylib';
import { named1, named2 } from 'src/mylib';
// Renaming: import named1 as myNamed1
import { named1 as myNamed1, named2 } from 'src/mylib';
// Importing the module as an object
// (with one property per named export)
import * as mylib from 'src/mylib';
// Only load the module, don’t import anything
import 'src/mylib';
Exporting examples
// export inline
export var myVar1 = ...;
export let myVar2 = ...;
export const MY_CONST = ...;
export default 123;
// define everyting and export at the last
export { MY_CONST, myFunc };
// export things under different names:
export { MY_CONST as THE_CONST, myFunc as theFunc };
ReExporting examples Re-exporting means adding another module’s exports to those of the current module.
// add all of the other module’s exports:
export * from 'src/other_module';
// be more selective
export { foo, bar } from 'src/other_module';
// Export other_module’s foo as myFoo
export { foo as myFoo, bar } from 'src/other_module';
// Re-export other_module's default export
export { default } from 'src/other_module';
The above exampled are taken from the wonderful 2ality post
Module loading techniques summary#
Runs on | Loaded | Filename ext. | |
---|---|---|---|
Script | browsers | async | .js |
CommonJS module | servers | sync | .js .cjs |
AMD module | browsers | async | .js |
ECMAScript module | browsers and servers | async | .js .mjs |