If you are not familiar with javascript module, I highly recommend you must visit here
All the module patterns that you have seen here, have one thing in common: the use of single global variable to wrap its code in a function and thereby creating a private namespace for itself using a closure scope. This is the great advantage of these types of module patterns.
-
managing dependencies :- Let's say we have two javascript file, main.js and script.js, in which main.js depends on the code of script.js. For this, script.js should load first i.e, script.js should be included before main.js. What if we have a large project, it becomes quite difficult for developers to maintain this.
-
Namespace collisions :- What if we have two modules with same name. This will create an issue
-
Version collisons :- What if we have two versions of the module and we want to use both of them.
Solution to the above problem is to design a way to ask for a module's interface without going through the global scope. And this can be done through well-implemented approaches : CommonJS and AMD
A commonJS module is a reusable piece of Javascript which exports specific object which is avilable to require in other programs. If you are familiar with Node.js, you can relate this.
function commonModule(){
this.hello = function(){
return 'Hello!'
}
this.bye = function(){
return 'Bye!'
}
}
module.exports = commonModule
//var commonModule = require('path_to_commonModule')
var commonModule = require('commonModule')
var myModule = new commonModule()
commonModule.hello() //'Hello'
commonModule.bye() //'Bye'
- Avoiding global namespace pollution
- Making our dependencies explicit
- Compact syntax
- Module loads synchronously
- Only support objects as modules
- Reading a module from web takes longer then reading from disc and due to synchronous property, as long as the script to load a module is running, it blocks the browser from running anything else until it finishes loading.
Although commonJS technique was good, but what if we want to load modules asynchronously? This can be done through AMD
Code to module named myModule
define([], function(){
return {
hello: function(){
console.log("Hello!")
},
bye: function(){
console.log('Bye!')
}
}
})
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule){
console.log(myModule.hello())
})
define function an array of module's dependencies as its first argument and these arguments are loaded in the background in a non-blocking manner. Once it loaded, define call the callback function it was given with argiment myModule and myOtherModule which were loaded earlier
- Takes browser-first approach
- Asynchronous behaviour
- Modules can be objects, functions, constructors, strings, JSON and many other types
- Not compatible with io, filesystem and other server-oriented features
- Function wrapping syntax is a bit more verbose compared to a simple require statement
UMD or Universal Module Definition is used in a project which requires the feature of both commonJS and AMD alonwith the support of global variable definition.
UMD modules are capable of working on both client and server
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['myModule', 'myOtherModule'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('myModule'), require('myOtherModule'));
} else {
// Browser globals (Note: root is window)
root.returnExports = factory(root.myModule, root.myOtherModule);
}
}(this, function (myModule, myOtherModule) {
// Methods
function notHelloOrGoodbye(){}; // A private method
function hello(){}; // A public method because it's returned (see below)
function goodbye(){}; // A public method because it's returned (see below)
// Exposed public methods
return {
hello: hello,
goodbye: goodbye
}
}));
For more information regarding UMD, visit UMD
As you might have noticed above, none of the modules above were native to JavaScript. Instead of this, we have created ways to emulate a modules system by using either the module pattern, commonJS or AMD.
There comes a built-in modules with ECMAScript 6 (ES6) intorduced by smart folks at TC39 (the standards body that defines the syntax and semantics of ECMAScript)
1. CommonJS module
// lib/counter.js
var counter = 1;
function increment() {
counter++;
}
function decrement() {
counter--;
}
module.exports = {
counter: counter,
increment: increment,
decrement: decrement
};
// src/main.js
var counter = require('../../lib/counter');
counter.increment();
console.log(counter.counter); // 1
In this example, we basically make two copies of the module: one when we export it, and one when we require it.
Moreover, the copy in main.js is now disconnected from the original module. That’s why even when we increment our counter it still returns 1 — because the counter variable that we imported is a disconnected copy of the counter variable from the module.
So, incrementing the counter will increment it in the module, but won’t increment your copied version. The only way to modify the copied version of the counter variable is to do so manually:
counter.counter++; //it will increment exported counter
console.log(counter.counter); // 2
2. ES6 module ES6 creates a live read-only view of the modules we import:
// lib/counter.js
export let counter = 1;
export function increment() {
counter++;
}
export function decrement() {
counter--;
}
// src/main.js
import * as counter from '../../counter';
console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2
As you can clearly see the difference between CommonJS module and ES6 module, how ES6 module can rule over CommonJS module.
Benefits
- Compact and declarative syntax
- Asynchronous loading
- Better support for cyclic dependencies
- Imports are live read-only view of exports
Yay! We learnt all the types of modules in Javascript.
Contributor : Harsh Anand