Modules in javascript. Template Module. And its formats: CommonJS, AMD, UMD

Lecture



A module is a piece of code that encapsulates implementation details and provides an open API for use by other code.

JavaScript initially did not have built-in support for modules, unlike most other programming languages, but with the complication of the frontend, the need to structure the code became obvious.

This led to the appearance of the “Module” programming pattern, and then of separate formats: CommonJS, AMD, UMD, and special tools for working with them. The native system of modules in JavaScript was added to the ECMAScript 6 specifications, and browser developers are working on supporting it.

The first use of this approach was noted in 2003, when Richard Cornford cited it as an example of the use of closures.

In 2005-2006, the developers of YUI used this approach for their framework.

The “Module” template was most popular after Douglas Crockford described it in his book “The JavaScript The Good Part”.

Template Module

The "Module" template is based on an immediately called function (IIFE):

Javascript
var MODULE = (function () {
var privateVariable = 1;

function privateMethod () {
// ...
}

return {
moduleProperty: 1,
moduleMethod: function () {
// ...
}
};
} ());
Immediately called function forms a local scope in which you can declare the necessary private properties and methods. The result of the function is an object containing public properties and methods.

By passing the parameters to the immediately called function, you can import the dependencies into the module. This increases the speed at which the variables are resolved, since the imported values ​​become local variables inside the function.

Javascript
(function ($) {
// ...
} (jQuery));

If you need to perform without immediate call, you can use objects with properties from Javascript functions


 


window.SomeModule = {
someBlock1: '[data-first-Block]',
someBlock2: '[data-second-block]',

buttons: {
botton1: '[data-botton1]',
botton2: '[data-botton2]'
},
urls: {
action1Url: '/url1',
action2Url: '/url2',
},
init: function () {
this.initActions();
},
initActions: function () {
var $this = this;

$('body').on('click', this.buttons.botton1, function () {
$this.add($(this).data('id'));
});

$(document).ready(function () {
$this.initPlugin();
});


},
initPlugin: function () {
var $this = this;

},
add: function (id, options) {
var $this = this;

},

remove: function (id) {
var $this = this;
if (!$this.pending) {
$this.pending = true;
$.ajax({
type: 'DELETE',
dataType: 'json',
url: this.urls.action1Url+ '/' + id,
}).done(function (res) {

}).fail(function (err) {

});
}
},

};

for call

SomeModule.init()

Module Formats


CommonJS
From a structural point of view, a CommonJS module is part of JavaScript code that exports certain variables, objects, or functions, making them available to any dependent code.

CommonJS modules consist of two parts: module.exports contains the objects that the module wants to make available, and the require () function is used to import other modules.

An example of a module in CommonJS format:

Javascript
// define the function we want to export
function foobar () {
this.foo = function () {
console.log ('Hello foo');
}

this.bar = function () {
console.log ('Hello bar');
}
}

// make it available to other modules
module.exports.foobar = foobar;
Sample code using the module:

Javascript
var foobar = require ('./ foobar'). foobar,
test = new foobar ();

test.bar (); // 'Hello bar'
AMD
The AMD format (Asynchronous Module Definition) is based on two functions: define () to define named or nameless modules and require () to import dependencies.

The define () function has the following signature:

Javascript
define (
module_id / * optional * /,
[dependencies] / * optional * /,
definition function / * function to create an instance of a module or object * /
);
The module_id parameter is optional, it is usually only required when using non-AMD merge tools. When this argument is omitted, the module is called anonymous. The dependencies parameter is an array of dependencies that are required by the designated module, and the third argument (definition function) is a function that is executed to create an instance of the module.

Module example:

Javascript
define ('myModule',
['foo', 'bar'],
// dependencies (foo and bar) are passed to the function
function (foo, bar) {
// create a module
var myModule = {
doStuff: function () {
console.log ('Hi!');
}
}

// return the module
return myModule;
}
);
The require () function is used to import modules:

Javascript
require (['foo', 'bar'], function (foo, bar) {
foo.doSomething ();
});
Also with the help of require () you can dynamically import dependencies into the module:

Javascript
define (function (require) {
var foobar;

require (['foo', 'bar'], function (foo, bar) {
foobar = foo () + bar ();
});

// return the module
// notice another module definition pattern
return {
foobar: foobar
};
});


UMD


The existence of two module formats that are incompatible with each other did not contribute to the development of the JavaScript ecosystem. To solve this problem, the Universal Module Definition (UMD) format was developed. This format allows you to use the same module with both AMD tools and CommonJS environments.

The essence of the UMD approach is to verify the support of a particular format and declare the module accordingly. An example of such an implementation:

Javascript
(function (define) {
define (function () {
var bar = 'foo';

return {
foo: function () {
// ...
}
};
});
} (
typeof module === 'object' && module.exports && typeof define! == 'function'?
function (factory) {module.exports = factory (); }:
define
))
UMD is more an approach than a specific format. There may be many different implementations.

ECMAScript 2015 Modules


In the ECMAScript 2015 standard, native JavaScript modules have appeared. Currently, ES6 modules are supported in Safari 10.1 and behind the flag in Firefox 54, Chrome 60 and Edge 15.

The ES6 modules are based on export and import keywords. Any variable declared in a module is available outside of it only if explicitly exported from a module.

Syntax
If the module exports only one value, you can use the default export. For example, the module exports the function:

Javascript
export default function () {··}
Or class:

Javascript
export default class {···}
Or even the expression:

Javascript
export default 5 * 7;
One module can export several values:

Javascript
export const pi = Math.PI;
export function sum (x, y) {
return x + y;
}
export function multiply (x, y) {
return x * y;
}
You can list everything you want to export at the end of the module:

Javascript
export const pi = Math.PI;
export function sum (x, y) {
return x + y;
}

export {pi, sum};
And rename:

Javascript
export {pi as PI, sum};
You can also import modules in several ways:

Javascript
// Import default value
import localName from 'utils';

// Import individual functions
import {sum, multiply} from 'utils';
sum (4, 3);

// Import the entire module
import * as utils from 'utils';
utils.sum (4, 3);

// You can rename the imported value
import {pi as PI, sum} from 'utils';

// Or import nothing
// (in this case, the module initialization code will be executed,
// but nothing will be imported)
import 'utils';
Subtleties
Import and export keywords can be used only at the top level, they cannot be used in a function or in a block:

Javascript
if (Math.random ()) {
import 'foo'; // SyntaxError
}
Import from the module rises to the beginning of the scope:

Javascript
foo ();

import {foo} from 'test_module';
ES6 modules are deferred only when the document is fully analyzed.

Module code is executed in strict mode.

Module Loaders


AMD and CommonJS are formats of modules, not implementations. For support of AMD, for example, the implementation of the functions define () and require () is necessary, for support of CommonJS - the implementation of module.exports and require ().

To support modules at runtime, module loaders are used. There are several different downloaders, they have a similar principle of operation:

You connect the bootloader script in the browser and tell it which file to download as the main one.
The module loader loads the main application file.
The module loader loads other files as needed.
Popular module loaders:

RequireJS loads modules in AMD format.
curl.js loads AMD and CommonJS modules.
SystemJS loads AMD and CommonJS modules.
Module builders
Unlike module loaders, which work in the browser and load dependencies on the fly, module builders allow you to prepare one file with all dependencies (bundle) in advance.

There are a number of tools that allow you to pre-assemble modules into one file:

Browserify supports CommonJS format.
Webpack supports AMD, CommonJS and ES6 modules.
Rollup supports ES6 modules.


Conclusion


To better navigate modern front-end development tools, it is necessary to understand such concepts as modules, module formats, module loaders and assemblers.

So:

A module is a reusable piece of code that encapsulates implementation details and provides an open API.

Module format is the syntax for defining and connecting a module.

The module loader loads a module of a specific format at runtime directly in the browser. Popular bootloaders are RequireJS and SystemJS.

Module builder pre-merges modules into one file, which is connected on the page. Examples of collectors are Webpack and Browserify.


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Scripting client side JavaScript, jqvery, BackBone

Terms: Scripting client side JavaScript, jqvery, BackBone