Open anitsh opened 4 years ago
JavaScript modules are needed for encapsulation and dependency management. Encapsulation is essential to prevent conflicts and ease development. When it comes to dependencies management, in traditional client-side JavaScript development, they are implicit. In other words, it is the job of the developer to make sure dependencies are scripts are manually loaded in the correct order.
As JavaScript development gets more and more complex, dependency management can get cumbersome. Refactoring is also impaired: where should newer dependencies be put to maintain proper order of the load chain?
JavaScript module systems attempt to deal with these problems and others. They were born out of necessity to accommodate the ever growing JavaScript landscape. Different solutions were created to solve this problem.
JavaScript scopes worked at the function level until at least up to the appearance of let
in ES2015.
The revealing module pattern relies on functions to encapsulate private contents. Whatever binding is declared inside a function cannot escape its scope. It is for this reason the revealing module pattern relies on functions to encapsulate private contents.
Example from Addy Osmani's JavaScript Design Patterns book.
var myRevealingModule = (function () {
var privateVar = "Ben Cherry",
publicVar = "Hey there!";
function privateFunction() {
console.log( "Name:" + privateVar );
}
function publicSetName( strName ) {
privateVar = strName;
}
function publicGetName() {
privateFunction();
}
// Reveal public pointers to
// private functions and properties
return {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
})();
CommonJS aims to define a series of specifications to help in the development of server-side JavaScript applications. CommonJS modules were designed with server development in mind. Naturally, the API is synchronous. In other words, modules are loaded at the moment and in the order they are required inside a source file.
One of the areas the CommonJS team attempts to address are modules. Node.js developers originally intended to follow the CommonJS specification but later decided against it. When it comes to modules, Node.js's implementation is very influenced by it. There are abstractions on top of Node.js's module system in the form of libraries that bridge the gap between Node.js's modules and CommonJS. For the purposes of this post, we will only show the basic features which are mostly the same.
// In circle.js
const PI = Math.PI;
exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;
// In some file
const circle = require('./circle.js');
console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);
AMD was born out of a group of developers that were displeased with the direction adopted by CommonJS. In fact, AMD was split from CommonJS early in its development.
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 () {};
});
Asynchronous loading is made possible by using JavaScript's traditional closure idiom: a function is called when the requested modules are finished loading. Module definitions and importing a module is carried by the same function: when a module is defined its dependencies are made explicit. An AMD loader can therefore have a complete picture of the dependency graph for a given project at runtime. Libraries that do not depend on each other for loading can thus be loaded at the same time. This is particularly important for browsers, where startup times are essential to a good user experience.
Implementations of AMD are require.js and Dojo
Fortunately, the ECMA team behind the standardization of JavaScript decided to tackle the issue of modules. The result can be seen in the latest release of the JavaScript standard: ECMAScript 2015 (previously known as ECMAScript 6). The result is syntactically pleasing and compatible with both synchronous and asynchronous modes of operation.
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
The import
directive can be used to bring modules into the namespace. This directive, in contrast with require
and define
is not dynamic (i.e. it cannot be called at any place). The export
directive, on the other hand, can be used to explicitly make elements public.
A draft of dynamic loading via import
https://github.com/tc39/proposal-dynamic-import
In truth, ES2015 only specifies the syntax for static module loaders. In practice, ES2015 implementations are not required to do anything after parsing these directives. Module loaders such as System.js are still required. This solution, by virtue of being integrated in the language, lets runtimes pick the best loading strategy for modules. In other words, when asynchronous loading gives benefits, it can be used by the runtime.
Unfortunately none of the major JavaScript runtimes support ES2015 modules in their current stable branches. This means no support in Firefox, Chrome or Node.js. Fortunately many transpilers do support modules and a polyfill is also available. Currently, the ES2015 preset for Babel can handle modules with no trouble.
System.js: a universal module loader that supports CommonJS, AMD and ES2015 modules. It can work in tandem with transpilers such as Babel or Traceur and can support Node and IE8+ environments.
<script src="system.js"></script>
<script>
// set our baseURL reference path
System.config({
baseURL: '/app',
// or 'traceur' or 'typescript'
transpiler: 'babel',
// or traceurOptions or typescriptOptions
babelOptions: {
}
});
// loads /app/main.js
System.import('main.js');
</script>
As System.js does all the job on-the-fly, using ES2015 modules should generally be left to a transpiler during the build step in production mode. When not in production mode, System.js can call the transpiler for you, providing seamless transition between production and debugging environments.
JavaScript a dialect of the ECMAScript language. JavaScript is the coffee-flavored language with which I love to program. ECMAScript is the specification it’s based on. By reading the ECMAScript specification, you learn how to create a scripting language. By reading the JavaScript documentation, you learn how to use a scripting language.
A JavaScript engine is a program or interpreter that understands and executes JavaScript code.Synonyms JavaScript interpreter, JavaScript implementation. JavaScript engines are commonly found in web browsers, including V8 in Chrome, SpiderMonkey in Firefox, and Chakra in Edge. Each engine is like a language module for its application, allowing it to support a certain subset of the JavaScript language.A JavaScript engine to a browser is like language comprehension to a person.
A transpiler that can convert ES6 code to ES5 code, for example Babel.