Evolution

of

JavaScript modules

 

 

-  Lalitha Iyer

         @Iyer_Lalitha1
 

About Me

  • Software developer for ~ 7 years 

  • Javascript for ~ 4 years
  • On Netflix's JS Bundling team ~1.5 years
  • Love traveling
  • Learning new languages ;) 
    • both spoken and programming

 

Key Concepts

  • several smaller modules are composed together, to form an application.

 

  • ​the assembling of the app can be compile-time or runtime.

 

 

 
 

Modularity as a concept dates back to as early as 1950 with languages like JOVIAL & COBOL that provided library capabilities

 

Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality. " - Wikipedia

References

References

https://en.wikipedia.org/wiki/Asynchronous_module_definition

https://en.wikipedia.org/wiki/Asynchronous_module_definition

 

 

Module Themes

Explicit Dependency References

&
Module Graph

 

 

Root

Module6

Module1

Module2

Module4

Module5

Evolution of JavaScript

1995

Birth of Javascript

1997

EcmaScript​ 

1999 

ES3 released

2005

 AJAX & Gmail
 

2009-2014

ServerJS/Common JS

Node.js

NPM

vendor wars

standardization

jQuery

CSS frameworks

< script >

global namespace

Object Literal

module namespace

Module Pattern
IIFE

CJS, AMD

module loaders

browserify
webpack

module bundlers

js on server

2015

ES6

SPA frameworks

 

Bower

    front end

    package manager

server

client

scripting

        SoC,  Encapsulation 

 &

          NameSpacing

            Standardized Modules

SPA

es module

Evolution of Modules

Encapsulation

Namespacing

Explicit dependency references

Explicit export

js on many platforms

2019

ES modules supported on  browser and node

es modules

CJS, AMD

References

<html>
<head>
    <script type='text/javascript'>
        var onClick = function (type) {
            var name = document.getElementById('name').value;
            var greeting = 'hello'
            if (type === 'custom') {
                greeting = document.getElementById('greeting').value
            }
            showGreeting(getGreeting(name, greeting));
        }
        function getGreeting(name, greeting) {
            return name + '' + greeting
        }

        function showGreeting(greeting) {
            alert(greeting);
        }
    </script>
</head>
<body>
    <form>
        <input type='text' id='name'> Enter you name </input>
        <input type='text' id='greeting'> Enter you Greeting </input>

        <button onclick='onClick()'>Show Greeting</button>
        <button onclick='onClick("custom")'>Show Custom Greeting</button>

    </form>
</body>
</html>

Inline Script 

<!–– Greeting Applicaiton -->
<html>
<head>
    <script src='./greeting.js' type='text/javascript'></script>
    <script>
        var onClick = function (type) {
            var name = document.getElementById('name').value;
            var greeting = 'hello'
            if (type === 'custom') {
                greeting = document.getElementById('greeting').value
            }
            showGreeting(getGreeting(name, greeting));
        }
    </script>
</head>
<body>
    <form>
        <input type='text' id='name'> Enter you name </input>
        <input type='text' id='greeting'> Enter you Greeting </input>

        <button onclick='onClick()'>Show Greeting</button>
        <button onclick='onClick("custom")'>Show Custom Greeting</button>

    </form>
</body>
</html>

External Script 

 
// Greeting.js

function getGreeting(name, greeting) {
    return name + '' + greeting
}

function showGreeting(greeting) {
    alert(greeting);
}
var greetingObject = {
  name: "",
  greeting: "hello",
  init: function(name, greeting) {
    this.name = name; 
    this.greeting = greeting; 
  },
  getGreeting: function() {
    return this.greeting + " " + this.name;
  },
  showGreeting: function() {
    alert(this.getGreeting());
  },
  printGreeting: function() {
    console.log(this.getGreeting());
  }
};

Object Literal & Namespacing 

var onClick = function (type) {
    var name = document.getElementById('name').value;
    var greeting = 'hello'
    if (type === 'custom') {
        greeting = document.getElementById('greeting').value
    }
    greetingObject.init(name, greeting);
    greetingObject.showGreeting(greetingModule.getGreeting();
}

Functions 

  • can be passed as arguments
  • can be return values
  • can include other function definitions
  • can be assigned to a variable
// namespacing  & encapsulation
// Module pattern using IIFE
var greetingModule = (function() {
  // private, function scope
  var name = "";
  var greeting = "hello";

  // public
  function init(n, g) {
    name = n;
    greeting = g;
  }
  // private
  function getGreeting() {
    return greeting + " " + name;
  }

  // public
  function showGreeting() {
    alert(getGreeting());
    printGreeting(getGreeting());
  }

  // Object literal
  return {
    init,
    showGreeting
  };
})();
<html>
<head>
    <script src='./greeting-module.js' type='text/javascript'></script>
    <script type='text/javascript'>
        var onClick = function (type) {
            var name = document.getElementById('name').value;
            var greeting;
            if (type === 'custom') {
                greeting = document.getElementById('greeting').value
            }
            greetingModule.init(name, greeting);
            greetingModule.showGreeting();
        }
    </script>
</head>
<body>
    <form>
        <input type='text' id='name'> Enter you name </input>
        <br /> <br />
        <input type='text' id='greeting'> Enter you Greeting </input>
        <br /> <br />
        <button onclick='onClick()'>Greeting</button>
        <button onclick='onClick("custom")'>Custom Greeting</button>
    </form>
</body>
</html>

Module IIFE Pattern

Closure & IIFE

 

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state

"  -MDN

Function Definition 

function sayHi() {
    alert("Hello, World!");
}
// Function expression
var sayHi = function() {
    alert("Hello, World!");
}

// Function expression
(function(){
alert('hello world')
}) 

// IIFE
(function(){
alert('hello world')
})() 

Function Expression

References

Glossary

Common JS

 
// display.js
let displayFunction;
if (process) {
  displayFunction = console.log;
} else {
  displayFunction = alert;
}

module.exports = displayFunction;
// greeting.js
const display = require("./display");

let name = "";
let greeting = "hello";

function init(name, greeting) {
  name = name;
  greeting = greeting;
}

function getGreeting() {
  return greeting + " " + name;
}

function showGreeting() {
  display(getGreeting());
}

module.exports = {
  init,
  showGreeting
};

Explicit dependency reference

Explicit export

declaration

Wait !

How does this code run in a js environment

 

 

Loaders

- Node one of the  first the adopt
  

(function(require, module) {
  // display.js

  let displayFunction;

  //If node.js return function console.log
  if (!process.browser) {
    displayFunction = console.log;
  } else {
    displayFunction = alert;
  }

  module.exports = displayFunction;
})
let cache = {};

function require(moduleIdentifier) {

  // Resolve the module
  let resolvedModuleId = resolve(moduleIdentifier);
  
  // look up module cache
   if(cache[resolvedModuleId])
   	 return cache[resolvedModuleId];
   }
   
  // load module
  	let moduleSrc = load(resolvedModuleId);

  // Wrap source in a function expression
  let wrappedSource = wrap(moduleSrc);

  // Evaluate module
  let result = wrappedSource.call(require, {});

  // Cache result
  cache[moduleIdentifier] = result;
}

Require function - Module Loading

How Require Works - From James N. Snell

AMD 

// greeting.js
define(["display"], function(display) {
  return function() {
    let name = "";
    let greeting = "hello";

    function init(name, greeting) {
      name = name;
      greeting = greeting;
    }

    function getGreeting() {
      return greeting + " " + name;
    }

    function showGreeting() {
      display(getGreeting());
    }
    return {
      init,
      showGreeting
    };
  };
});
// display.js
define(function() {
  return function() {
    let displayFunction;
    if (process) {
      displayFunction = console.log;
    } else {
      displayFunction = alert;
    }
  };
});
// main.js
define(["greeting"], function() {
  return function() {
    let submitButton = document.querySelector(".submit");
    submitButton.addEventListener("click", () => {
      showGreeting(document.getElementById("name").value);
    });
  };
});

AMD Define

Title Text

<html>

<head>
    <script data-main="main" src="scripts/require.js"></script>
</head>

<body>
    <form>
        <input type='text' id='name'> Enter you name </input>
        <button id='greeting'>Greeting</button>

    </form>
</body>

</html>
// main.js

const greetingModule = require("./greeting");

let submitButton = document.querySelector(".submit");

submitButton.addEventListener("click", () => {
  showGreeting(document.getElementById('name').value);
});


module.exports = {};

// We use a bundler to assemble the modules in a single bundle 
browserify main -o bundle.js


Module6

Module4

Module5

<html>
<head>
    <script src='./bundle.js' type='text/javascript'></script>
</head>
<body>
    <form>
        <input type='text' id='name'> Enter you name </input>
        <button id='greeting'>Greeting</button>

    </form>
</body>
</html>

main

greeting

display

(function(modules) {
  // The module cache
  var installedModules = {};

  function __webpack_require__(moduleId) {
    // Check if module is in cache
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    } // Create a new module (and put it into the cache)
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    }); // Execute the module function
    modules[moduleId].call(__webpack_require__, module);
    return module.exports;
  }
  // execute the entry point
  return __webpack_require__("main.js");
})({
  "./display.js": (function(require, module) {
        let displayFunction;
        if (process) {
        displayFunction = console.log;
        } else {
        displayFunction = alert;
        }
        module.exports = displayFunction;    
  }),
  
  "./greeting.js": (function(require, module) { // src }),

  "./main.js": (function(require, module) { // src })
});

webpack bundle

/***** greeting.mjs ******/
import { display } from "./display";

let name = "";
let greeting = "hello";

function init(name, greeting) {
  name = name;
  greeting = greeting;
}

function getGreeting() {
  return greeting + " " + name;
}

function showGreeting() {
  display(getGreeting());
}

export { init, showGreeting };
/*** display.mjs ***/
let display;
if (process) {
  display = console.log;
} else {
  display = alert;
}

export { display };
/** main.mjs **/
import { showGreeting } from "./greeting";

let submitButton = document.querySelector(".submit");

submitButton.addEventListener("click", () => {
  showGreeting(document.getElementById('name').value);
});
<html>

<head>
    <script type="module" src="main.mjs"></script>
</head>

<body>
    <form>
        <input type='text' id='name'> Enter you name </input>

        <input type='text' id='greeting'> Enter you Greeting </input>

        <button onclick='onClick()'>Greeting</button>

        <button onclick='onClick("custom")'>Custom Greeting</button>
    </form>
</body>

</html>

Common JS / AMD

 

  • Supports dynamic requires and exports
    • require(`$foo/bar.js`)
  • module format of the present

 

 

  • Only support
    • static imports
    •  named exports
      • static analysis tools
      • advanced bundling to elimimate dead code is possible
    • moulde format of the present and future

 

 

 

Conclusions

The Best is yet to Come !

Raise the level of Abstraction

Thank You

Lalitha Iyer

@Iyer_Lalitha1

Evolution of modules

By Lalitha Iyer

Evolution of modules

  • 1,553