Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Totally Modular, Dude!

DZone's Guide to

Totally Modular, Dude!

Using JavaScript for that web application you are building but don't know much about how to use modules to your advantage? We'll cover all the basics you need to get started with them.

· Web Dev Zone
Free Resource

Get deep insight into Node.js applications with real-time metrics, CPU profiling, and heap snapshots with N|Solid from NodeSource. Learn more.

This article is featured in the new DZone Guide to Web Development: Applications and Frameworks, Volume I. Get your free copy for more insightful articles, industry statistics, and more!

If you’re building a web application today, chances are there’s a lot of JavaScript involved. Keeping that JavaScript properly structured so that it’s easy to understand, maintain, and change over time is a big challenge. That’s why, since the mid-late 2000s most web applications have taken a modular approach.

JavaScript code went from being written as a long list of functions and variables all contained in a single file and attached to a single global scope, to being written as a set of independent, interchangeable modules that each contain all the code necessary to deliver only one aspect of an application’s complete, desired functionality.

The trouble is, until the advent of ES2015, JavaScript didn’t inherently support this kind of separation, so developers had to rely on a number of clever workarounds and third-party libraries to make modules work.

Now that Modules are officially here, it’s worth exploring how best to use them.

What Are Modules?

The concept of Modules comes from Modular Programming.

Modular Programming is a software design technique in which the functionality of an application is separated into interchangeable, independent components. In Modular Programming each component is self-contained; meaning that each component contains everything necessary to execute a single, particular aspect of an application’s desired functionality. These components are
called “Modules.”

As of ES2015, JavaScript’s definition of a module is far more broad; any file containing exclusively JavaScript code is considered a module.

The Benefits of Modular Programming

Modular programming has a number of benefits:

Readability

Suppose that the entire text of this article was presented to you as a single, extremely long paragraph. No subheadings. No line breaks. Just an unending stream of words. Chances are that would make it very hard – and quite unpleasant – to read and understand. So, instead, this article contains a number of paragraphs all neatly divided into sections.

Likewise, separating an application into modules makes the application easier to understand and reason about.

This enhanced readability, in turn, makes it easier to find bugs and also to work with legacy code.

Collaboration

By breaking an application up into several independent modules, multiple developers can work together on the same application with less fear of damaging or interfering with each other’s code.

Reusability

Because of the self-contained nature of Modules, Modular Programming lends itself to the creation of highly re-usable components. In JavaScript, this is evidenced by the rise in popularity of package managers like Bower and npm.

Encapsulation

Modules can be selective about what information and functionality they make available to other modules. This prevents unintended consequences and bugs by increasing predictability and control.

Specifically in JavaScript, before ES2015, anything declared outside the scope of a function was automatically added to the global scope. When working with third-party code or even when simply collaborating with other developers on a large project, the risk of name collisions and overwriting values was high.

To solve this, developers resorted to building modules using complicated patterns like Immediately Invoked Function Expressions (IIFE), Asynchronous Module Definitions (AMD), Universal Module Definitions (UMD), and Common JS (see Appendix A for details).

In ES2015 this paradigm is inverted. Each module creates its own lexical scope, meaning that nothing declared within a module is available or visible to any other module unless it is explicitly exported.

ES2015 Export and Import Syntax

As part of the formal introduction of modules into the JavaScript language, the ES2015 Language Specification also introduced syntax for importing and exporting values to and from modules. The full details can be found in section 15.2.2 and 15.2.3 of the EcmaScript 2015 Language Specification. Below is a summary of the new syntax.

:: Export Syntax

// default exports
    export default 42;
    export default {};
    export default [];
    export default (1 + 2);
    export default foo;
    export default function () {}
    export default class {}
    export default function foo () {}
    export default class foo {}
// inline exports
    export var foo = 1;
    export var foo = function () {};
    export var bar;
    export let foo = 2;
    export let bar;
    export const foo = 3;
    export function foo () {}
    export class foo {}
// named exports
    export {};
    export {foo};
    export {foo, bar};
    export {foo as bar};
    export {foo as default};
    export {foo as default, bar};
// exports from (re-exports)
    export * from “foo”;
    export {} from “foo”;
    export {foo} from “foo”;
    export {foo, bar} from “foo”;
    export {foo as bar} from “foo”;
    export {foo as default} from “foo”;
    export {foo as default, bar} from “foo”;
    export {default} from “foo”;
    export {default as foo} from “foo”;

:: Import Syntax

// default imports
    import foo from “foo”;
    import {default as foo} from “foo”;
// named imports
    import {} from “foo”;
    import {bar} from “foo”;
    import {bar, baz} from “foo”;
    import {bar as baz} from “foo”;
    import {bar as baz, xyz} from “foo”;
// glob imports
    import * as foo from “foo”;
// mixing imports
    import foo, {baz as xyz} from “foo”;
    import foo, * as bar from “foo”;
// just import
    import “foo”;

Source: https://github.com/eslint/espree/pull/43

Browser and Node.js Support

Edge already has experimental support for the import syntax and both Chrome and Mozilla have announced their intent to support the newly proposed <script type=”module”> specification which will add native support for modules in web browsers.

Node.js has not yet committed to adopting the ES2015 standard, but there is a solid proposal
in place and a good deal of pressure from the community.

In the meantime, a transpiler (e.g. Babel) is needed in order to use the ES2015 module syntax consistently across all environments and in the browser this should be mixed with a module bundler like Webpack or SystemJS.

Best Practices

Use the ES2015 Syntax

Despite the current lack of support across browsers and Node.js, now that there is a standard built into the language, it is likely to become the most stable, most widely supported way to write and use JavaScript modules over time.

Use the ES2015 syntax in combination with a transpiler and/or bundler to make your projects “future-ready.”

For legacy projects that already use modules with a different syntax, both SystemJS and Webpack when combined with Babel support projects with mixed module definitions. On the server-side Babel provides support for transpiling ES2015 module definition to CommonJS and also supports mixed module definitions.

Strive for a Single Default Export per Module

One of the explicit design goals of ES2015 was to favor default exports; therefore, ES2015 has the most straightforward syntax for default exports.

This implies that modules should have a one-to-one relationship with a self-contained construct such as a function, object, or class.

Export a Single Object That Represents the Module's Public API

Inspired by Christian Heilmann’s Revealing Module Pattern, an elegant way to write a self-documenting module that makes its API clear and obvious is to encapsulate all publicly available functions and values in a single object and then to expose that object as the default export.

The use of ES2015’s shorthand declaration syntax for object literals makes this technique even more powerful.

Consider the following contrived calculator module...

// calculator.js
    const api = { add, subtract, multiply, divide };
    function add(a,b) {...}
    function subtract(a,b) {...}
    function m ultiply(a,b) {...}
    function divide(a,b) {...}
    function somePrivateHelper() {...}
    export default api;

Defining the API near the top of the module allows anyone reading the code to immediately understand its interface without the need to read the entire listing.

Note: One might expect that an ES2015 destructuring syntax would work when importing this module, but in fact, it does not. Specifically, the following line will fail to work as expected:

import ( add, subtract } from ‘calculator’;

This is because the import syntax treats add and subtract as named exports, rather than the destructured properties of an exported object.

Export Factories for Complex Modules

Building upon the aforementioned recommendation, when dealing with a complex object it is advisable to export a single default factory function.

This allows for dependency injection and more sophisticated object construction while respecting the favored, single-default-export paradigm.

Consider the following (even more) contrived calculator module...

// calculatorFactory.js
    export default function(radix) {
        return {add,
            subtract,
            multiply,
            divide,
        };
        function add(a,b) {/* code that uses radix somehow */}
        function subtract(a,b) {/* code that uses radix somehow */}
        function multiply(a,b) {/* code that uses radix somehow */}
        function divide(a,b) {/* code that uses radix somehow */}
        function somePrivateHelper() {/* code that uses radix somehow */}
    }

Here again the module’s API is immediately apparent.

What About Exporting ES2015 Classes?

This author prefers not to use ES2015 classes, but that will have to be a topic for another day.

Conclusion

The ES2015 specification has finally brought real Modules to JavaScript, further cementing the advocacy of Modular Programming in the JavaScript community. Using Modular Programming in JavaScript promotes better readability, collaboration, reusability, and encapsulation.

While not yet fully supported, third-party libraries make it possible to start using ES2015 today. Adoption of the ES2015 Module standard will make applications “future-ready”. So, start now.

Appendix A - Pre-ES2015 Module Patterns

:: IIFE
    (function(){ /* code */ }());
:: AMD
    define(‘myModule’, [‘dep1’, ‘dep2’], function (dep1, dep2) {
    return function () {};
});
:: AMD - Simplified Common JS Wrapping
    define([‘require’, ‘dependency1’, ‘dependency2’], function
    (require) {
        var dependency1 = require(‘dependency1’),
        dependency2 = require(‘dependency2’);
    return function () {};
});
:: Common JS
    function myFunc(){ /* code */ };
        module.exports = myFunc;
:: UMD
    (function (global, factory) {
        if (typeof define === ‘function’ && define.amd) {
            // AMD
            define([‘jquery’], factory);
        } else if (typeof exports === ‘object’) {
            // Node, CommonJS-like
            module.exports = factory(require(‘jquery’)); 
        } else {
            // Browser globals (global is window)
            global.returnExports = factory(global.jQuery);
    }
}(this, function ($) {
    // methods
    function myFunc(){};
    // exposed public method
    return myFunc;
}));

References

ECMAScript® 2015 Language Specification

ecma-international.org/ecma-262/6.0/

Mozilla Developer Network - export

developer.mozilla.org/en/docs/web/javascript/reference/statements/export

Mozilla Developer Network - import

developer.mozilla.org/en/docs/web/javascript/reference/statements/import

ECMAScript 6 Modules: the final syntax

2ality.com/2014/09/es6-modules-final.html

Intent to implement <script type=”module> (Chrome)

groups.google.com/a/chromium.org/forum/#!topic/blink-dev/uba6pMr-jec

Bug 1240072 -Implement milestone 0 <script type=”module”> (Mozilla)

bugzilla.mozilla.org/show_bug.cgi?id=1240072

HTML Living Standard – Script Element

html.spec.whatwg.org/#the-script-element:module-script

Again with the Module Pattern – reveal something to the world

christianheilmann.com/2007/08/22/again-with-the-module-pattern-reveal- something-to-the-world/

JavaScript Design Patterns

addyosmani.com/resources/essentialjsdesignpatterns/book

More Web Dev Goodness

For more insights on working with the latest frameworks using third-party APIs, implementing module JavaScript for clean and reusable code, and more, get your free copy of the new DZone Guide to Web Development!

If you'd like to see other articles in this guide, be sure to check out:

Node.js application metrics sent directly to any statsd-compliant system. Get N|Solid

Topics:
javascript ,modules ,web dev

Published at DZone with permission of Bill Sourour. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}