Organizing your Code: Javascript Modules
The way you structure your code is a key factor when you define your source code organization. It will make your life easier or miserable, and once this structure is established it will become a really tedious task to redefine it. That’s why you know about the different patterns available and choose the one fits your project best.
But, why should I care about modules? Well, is the way Javascript offers to organize and encapsulate your code, and if you don’t think you need to do it, because maybe it’s a small project or you don’t care at all, trust me, you are making a huge mistake!
Let’s start talking about what a module really is, and talking about Javascript modules is talking about Javascript scope: the only way to achieve privacy in Javascript code is enclosing the private code in a closure (an IIFE – Immediately Invoked Function Expression):
(function() {
// your code goes here
})();
And this is exactly all we are going to need to create blocks of encapsulated code, hidden one from another. But modules as we know them today are a little bit more: modules are ways of defining chunks of code that are dynamically loaded.
Currently there are three main different ways of defining a module:
AMD
Format proposed by Dojo. Uses XHR and eval to dynamically load code, allows defining module dependencies and has a great support for browser loaders (require.js, curl.js). It also can be used in server-side with require.js:
AMD module definition
// foo.js
define("foo", ["bar"], function(bar) {
return function() { ... };
});
AMD module requirement
// app.js
require(["foo"], function(foo) {
...
});
Common.JS
Born from a group of volunteer aiming to standardize modules and packages in Javascript. This is the pattern used by Node.js, but it’s also supported in browsers using curl.js or SproutCore:
CJS module definition
// foo.js
exports.foo = function() { ... }
CJS module requirement
// app.js
foo = require("./foo").foo
ES6 / ES Harmony
Next version of the language incorporates some new features, including, among others, a standard way to define and consume modules:
ES6 module definition
// foo.js
module foo {
export var foo = function() { ... };
}
ES6 module requirement
// app.js
module foo from "./foo" // remote URLs also allowed here
import * from foo;
Conclusion
AMD & CommonJS have a wide support, but in my opinion, each of them have a slightly different approach: AMD seems to fit better in browser-side (it’s inherently asynchronous) and CommonJS seems to fit better in server-side, where synchronously loading code is a choice. Of course, you can use third libraries to use them in whatever environment, but it doesn’t seem the natural way.
There are some hybrid approaches, so you don’t have to pick any them, but both, but this only makes things harder and difficult to follow.
Luckily, ES6 modules are (or will be) part of our day-to-day, so I strongly suggest this should be the way to go. Currently, browser support for ES6 modules is only achieved via transpilers or libraries, so we’ll have to wait a little bit longer to use this feature. V8 implementation is still incomplete, so no luck neither in server-side.
Until then, you’ll still have to choose a pattern to go with. I recommend you not to enter this holy war, but just to pick one of them and keep coding great stuff.
Bibliography:
- http://addyosmani.com/writing-modular-js/
- http://tagneto.blogspot.com.es/2011/04/on-inventing-js-module-formats-and.html
- http://requirejs.org/docs/whyamd.html