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.