Modules
Intro
During the course of JavaScript evolution, many ways of working with modules were developed. Currently the most notable are:
-
CommonJS (CJS) format used in Node.js (
module.exports
/require
), and -
ECMAScript modules (ESM) - ES6 (ES2015) native module format (
export
/import
).
Another library worth mentioning is Asynchronous Module Definition (AMD). Both CJS and AMD are supported by Webpack however version 2 of Webpack supports ESM natively and recommends it over CJS and AMD. Other known module formats are System.register and Universal Module Definition (UMD).
Only CommonJS and ECMAScript modules shall be explained herein.
CommonJS
CommonJS modules are loaded synchronously.
CommonJS makes each file to have its unique module context.
To export a default value from a module use module.exports = foo
.
// ./horses.js
const horses = ['Roach', 'Kelpie', 'Bucephalus']
module.exports = horses
// Alternatively: "module.exports = ['Roach', 'Kelpie', 'Bucephalus']".
// ./riders.js
const horses = require('./horses')
horses // => (3) ['Roach', 'Kelpie', 'Bucephalus']
typeof horses // => object
To export multiple values simultaneously use exports.foo = bar
.
// ./horses.js
const geraltsHorse = 'Roach'
const cirisHorse = 'Kelpie'
exports.geraltsHorse = geraltsHorse
exports.cirisHorse = cirisHorse
exports.alexandersHorse = 'Bucephalus'
// ./riders.js
const horses = require('./horses.js')
horses // => (3) ['Roach', 'Kelpie', 'Bucephalus']
typeof horses // => object'
The values exported with the statement exports
are being added to module.exports
property.
ESM: ES6 Module Format
Although ES6 module format is vastly used it is not yet supported by all browsers therefore using a transpiler might be needed to transpile it to a more generally supported format such as CJS or AMD.
With ES6 it is possible to export top level functions, variables, and classes.
Modules exported and imported with ES6 export statement are always in strict mode.
Default Export & Import
A file can include one ES6 default export.
// ./horses.js
const horses = ['Roach', 'Kelpie', 'Bucephalus']
export default horses
// Alternatively: export default ['Roach', 'Kelpie', 'Bucephalus']
// ./riders.js
import horses from './riders.js'
horses // => (3) ['Roach', 'Kelpie', 'Bucephalus']
typeof horses // => object
Named Exports
A file can include multiple ES6 named exports.
// .horses.js
export const roach = 'Roach'
export const kelpie = 'Kelpie'
export const bucephalus = 'Bucephalus'
// .riders.js
import { roach, kelpie, bucephalus } from './horses.js'
console.log(roach, kelpie, bucephalus) // Roach Kelpie Bucephalus
import * as horses from './horses.js'
console.log(horses) // Module {__esModule: true, Symbol(Symbol.toStringTag): 'Module'}
console.log(horses.roach) // Roach
Dynamic Import
It is possible to load modules asynchronously with the import
statement. Such an operation relies on promises.
Aggregating
It is possible import and export modules simultaneously to aggregate exports in a single module.
Module Loaders & Bundlers
A module loader is a program that loads and interprets modules at runtime. Examples: Require.js, SystemJS.
A module bundler bundles modules into a single module at build time. Examples: Browserify, Webpack.