JavaScript Module

loaders

May 2016

TehranJS

Seyed Naser Hassani

Freelance Frontend Developer

Before the topic,

There were something you may know…

What modules are?

Good authors divide their books into chapters and sections;

good programmers divide their programs into modules.

Why use modules?

good product made with

good parts.

good parts is:

CAPSULAR (self-contained)

Encapsulation,

is an Object Oriented Programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse.

Class

Variable

Method

Dependency WAR

<script src=“app.js”></script>
<script src=“jquery.min.js></script>

Uncaught Referencer Error: jQuery is not defined.

<script src=“jquery.min.js></script>
<script src=“app.js”></script>

encapsulation & dependency

Maintainability

Namespacing

Reusability

Modules resolve:

javascript module:

solutions

Ad-Hoc Solution:

Anonymous closure

(function () {
  // We keep these variables private inside this closure scope
  
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);
    
      return 'Your average grade is ' + total / myGrades.length + '.';
  }

  var failing = function(){
    var failingGrades = myGrades.filter(function(item) {
      return item < 70;});
      
    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());

}());

// ‘You failed 2 times.’

Anonymous closure

Ad-Hoc Solution:

Global Import

(function (globalVariable) {

  // Keep this variables private inside this closure scope
  var privateFunction = function() {
    console.log('Shhhh, this is private!');
  }

  // Expose the below methods via the globalVariable interface while
  // hiding the implementation of the method within the 
  // function() block

  globalVariable.each = function(collection, iterator) {
    if (Array.isArray(collection)) {
      for (var i = 0; i < collection.length; i++) {
        iterator(collection[i], i, collection);
      }
    } else {
      for (var key in collection) {
        iterator(collection[key], key, collection);
      }
    }
  };

  globalVariable.map = function(collection, iterator) {
    var mapped = [];
    globalUtils.each(collection, function(value, key, collection) {
      mapped.push(iterator(value));
    });
    return mapped;
  };

 }(globalVariable));

Global Import

Ad-Hoc Solution:

Object Interface

var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];

  // Expose these functions via an interface while hiding
  // the implementation of the module within the function() block

  return {
    average: function() {
      var total = myGrades.reduce(function(accumulator, item) {
        return accumulator + item;
        }, 0);
        
      return'Your average grade is ' + total / myGrades.length + '.';
    },

    failing: function() {
      var failingGrades = myGrades.filter(function(item) {
          return item < 70;
        });

      return 'You failed ' + failingGrades.length + ' times.';
    }
  }
})();

myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

Object Interface 

Ad-Hoc Solution:

The Revealing Module Pattern

var myRevealingModule = (function () {
    var privateVar = "JavaScript",
        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
    };
})();

myRevealingModule.setName( "Tehran JS" );

The Revealing Module Pattern 

but, we need more...

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Backbone.js Todos</title>
        <link rel="stylesheet" href="todos.css"/>
    </head>

    <body>
        <script src="../../test/vendor/json2.js"></script>
        <script src="../../test/vendor/jquery.js"></script>
        <script src="../../test/vendor/underscore.js"></script>
        <script src="../../backbone.js"></script>
        <script src="../backbone.localStorage.js"></script>
        <script src="todos.js"></script>
    </body>

    <!-- (...) -->

</html>

Backbonjs-todo.html

PROS

  • Simple enough to be implemented anywhere (no libraries, no language support required).
  • Multiple modules can be defined in a single file.

 

CONS

  • No way to programmatically import modules (except by usingeval).
  • Dependencies need to be handled manually.
  • Asynchronous loading of modules is not possible.
  • Circular dependencies can be troublesome.
  • Hard to analyze for static code analyzers.

javascript module:

other solutions

CommonJS or AMD or ES2015

To be or not to be,

?

CommonJS

// 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)}`);

CommonJS Module

  • Compact syntax
  • Designed for synchronous loading
  • Main use: server

implementation:

AMD (Asynchronous Module Definition)

//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 () {};
});

AMD Module

  • Slightly more complicated syntax, enabling AMD to work without eval() (or a compilation step).
  • Designed for asynchronous loading
  • Main use: browser

implementation:

ES2015 (native)

//------ 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

ES2015 Module

System.import('some_module')
    .then(some_module => {
        // Use some_module
    })
    .catch(error => {
        ...
    });

 Promise.all(
        ['module1', 'module2', 'module3']
        .map(x => System.import(x)))
    .then(([module1, module2, module3]) => {
        // Use module1, module2, module3
    });

ES2015 Module (Importing modules and loading scripts)

implementation:

  • Synchronous and asynchronous loading supported.
  • Syntactically simple.
  • Support for static analysis tools.
  • Integrated in the language (eventually supported everywhere, no need for libraries).

 

appendix:

  UMD (Universal Module Definition)

(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
  }
}));

UMD Module

console.thanks('THE END');

QUESTION?

JavaScript Module Loaders

By Naser Hassani

JavaScript Module Loaders

Learn about the different JavaScript module systems currently in use, and find out which will be the best option for your project.

  • 1,138