Need for modules.
Do-it-yourself modules.
Commonjs modules.
Asynchronous modules.
Concentrate on ES6 modules.
TypeScript modules.
Tom writes a file math.js
which includes a top-level sin()
function.
Jill writes a file morals.js
which includes a top-level sin()
function.
How does program reuse the code from both files?
This is a problem with programming in the large.
Common solution is to have programming language support modules; for example Java has packages and modules (since Java 9); C++ has namespaces.
The JavaScript language had no support for modules until ES6. In the interim, various ad-hoc solutions were provided by libraries.
JavaScript did not have modules until late in its lifetime. Hence module-like facilities were implemented within the language.
Code can be loaded and run in a browser using code like the following:
//Immediately Invoked Function Expression (IIFE) (function () { const constant = ...; let var = ...; function f1(element) { ... var ... constant ... } function f2(element) { ... f1(); ... var ... } //code which is run on current browser document f2(document.getElementById('shapes')); })();
const Math = (function() { function abs() { ... } ... function sin() { ... } ... return { abs, ..., sin, ... }; })(); const Morals = (function() { function doGood() { ... } ... function sin() { ... } ... return { doGood, ..., sin, ... }; })();
let x = ...; Math.sin(Math.PI/4); //ok Morals.sin('no more'); //ok Math.abs(x*3); //ok Morals.abs(...); //error Math.doGood(); //error
JavaScript is single threaded and can only be doing one thing at a time:
When a web page is loaded into a browser:
It may need to load multiple remote scripts and other resources.
If scripts are loaded synchronously, then browser will block during loading; this will result in unresponsive web pages.
Hence for a browser, external resources must be loaded asynchronously.
Resulted in Asynchronous Module Definitions AMD for use in browsers.
Circular dependencies problematic.
When a server-side application is started up, it is perfectly acceptable to wait for resources to be loaded into application. Hence synchronous loading is acceptable.
Distinguish between JavaScript scripts versus JavaScript modules.
Details of how a JavaScript program is recognized as a script or module depends on the JavaScript environment.
Within JavaScript modules, import
and export
statements are
recognized.
Within JavaScript scripts, import
and export
statements are
not recognized and will cause a syntax error.
There can only be a single ES6 module per file and a ES6 module is restricted to being defined within a single file.
All code within an ES6 module is always strict
.
Turned on by using
'use strict';
at the top-level or within a function.
Applies to entire script or individual functions.
Cleaned up some problematic semantics, made it easier for compilers to optimize code.
No accidental creation of global variables by silently assigning to an undeclared variable.
> (() => { 'use strict'; myVra = 42; })() Uncaught ReferenceError: myVra is not defined
Silent failure becomes an error: not allowed to assign to
non-writable property or constants like NaN
or undefined
.
> ((s) => { 'use strict'; s[0] = 'j'; }) ('hello') TypeError: Cannot assign to read only...
Problematic constructs like octal literals and with
not allowed:
> (() => { 'use strict'; return 077; })() SyntaxError: Octal literals are not allowed
All definitions within an ES6 module are private to that module
unless explicitly export
'ed.
Can export
each definition as it is made:
export const CONST = ... export class Class { ... } export function fn(...) { ... };
Alternately, can export a list of symbols:
const CONST = ... class Class { ... }; function fn(...) { ... }; export { CONST, Class, fn };
Can import features from an external module using an import
statement:
import { CONST, Class, fn } from './modules/module.js'; ... ... CONST + 2 ... ... new Class() ... ... fn() ...
Prefer to use a relative rather than absolute path to make it easier to move stuff around.
Can import entire module as an object:
import * as Module from './modules/module.js'; ... ... Module.CONST + 2 ... ... new Module.Class() ... ... Module.fn() ...
Can have a single default export
per module:
export default class { //anonymous class }
Import it giving it a name:
import ModuleClass from './modules/module.js'; ... new ModuleClass() ...
Can use renaming to avoid naming conflicts:
const CONST = ...; import { CONST as MODULE_CONST, Class, fn } from './modules/module.js';
Can use similar syntax for renaming in export
statements.
Dynamic imports possible by using asynchronous import()
function (as opposed to previously discusses import
statement).
Dynamic imports make it possible to import a module determined dynamically or conditionally.
// import nodejs path module into repl > let Path = (await import('path')).default undefined > Path.basename('/dir1/dir2/file.txt') 'file.txt' >
The dynamic import()
function can be used in both scripts and
modules whereas the static import
statement can only be used
within modules.
TypeScript supported modules since before modules were added officially to JavaScript.
TypeScript allows ES6 modules syntax.
Generated JS module code depends on TSC options:
target
Dialect of JS generated, values like es6
,
es2017
, es2022
and esnext
.
module
Values like commonjs
, es2022
.
Interacts with moduleResolution
.
moduleResolution
governs how the compiler finds files
corresponding to the import
strings. Main values
are classic
and node
with the latter being used
by modern code.
Modules can use extension .mjs
, but many tools do not
currently recognize that extension, so .js
still commonly
used.
On server-side, nodejs recognizes *.mjs
files as modules, but
can also recognize *.js
files as modules provided there is a
"type": "module"
declaration at the top-level within
package.json
.
Within browser, modules can be pointed to using
<script type="module">
.
Semantic Versioning attempts to avoid dependency hell. It uses a 3 part version number: M.m.r where each part is a integer without leading zeros.
Incremented for bug fixes.
Incremented for added functionality which is backward compatible.
Incremented for incompatible changes which are not backward compatible.
ES6 in Depth: Modules by Jason Orendorff.
Modules chapter in Exploring ES6 by Dr, Axel Rauschmayer.