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
- https://www.wired.com/2014/04/gmail-ten/
- cjs-effort-sets-javascript-on-path-for-world-domination
- http://wiki.commonjs.org/wiki/Modules
- history of javascript
- history of javascript modules
- https://addyosmani.com/writing-modular-js/
- CJS TC39 presentation
- https://www.blueskyonmars.com/2009/01/29/what-server-side-javascript-needs/
- https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
<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,760