The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Many Ways to Build Modular JavaScript
1. The Many Ways to Build
Modular JavaScript
Tim Perry
Tech Lead & Open-Source Champion at Softwire
@pimterry / github.com/pimterry / tim-perry.co.uk
2. JavaScript
Originally designed by Brendan Eich for Netscape in mid-1995 as
LiveScript, based on his ideas from Scheme and Self, and implemented
over 10 days ‘or something worse than JS would have happened’.
LiveScript ships in Netscape 2.0 that September
Renamed to JavaScript three months later to confuse as many people as
possible for marketing purposes
Standardisation begins a year later (now as ECMAScript), after everybody
has already implemented their own unique take.
First ECMAScript spec is published in June 1997.
16 years later, it’s the de facto standard language for all code on the
largest common platform in the world (and everybody still calls it
JavaScript)
3.
4. JavaScript
Some great bits:
Dynamic typing
First-order functions & closures
Some features that just need removing:
The ‘with’ keyword
Much of type coercion
Automatic semicolon insertion
Some fundamental structures that are hugely counter-intuitive:
How ‘this’ and variable scope work
Prototypes
Some clearly relevant features that don’t exist:
Simple class definitions
Tail-call optimizations
A mechanism to allow structured modular code
5. Why?
coffeeFunctions.js:
function askAboutSugar() { … }
function askAboutMilk() { … }
function prepareMug(sugar, milk) { … }
function requestuserEmptyGrounds() { … }
function requestuserEmptyTray() { … }
function grindBeans() { … }
function putGroundsInFilter() { … }
function heatWater() { … }
function waterHotEnough() { … }
function pourCoffeeInMug () { … }
function serveMugToUser() { … }
index.html:
<html><body>
<button onclick=‚makeCoffee()‛>Make Coffee</button>
<script src=‚coffeeFunctions.js‛></script>
<script>
function makeCoffee() {
var sugars = askAboutSugar();
var milk = askAboutMilk();
prepareMug(sugars, milk);
while (!groundsAreEmpty) requestUserEmptyGrounds();
while (!dripTrayIsEmpty) requestUserEmptyTray();
grindBeans();
putGroundsInFilter();
heatWater();
while (!waterHotEnough()) wait();
pourCoffeeInMug();
serveMugToUser();
};
</script>
</body></html>
6. Why?
JavaScript uses lexically-defined function scoping
Variables definitions are either in local scope or global scope
function f() {
var localVar = ‚a string‛;
}
function f(localParameter) {
localParameter = ‚a string‛;
}
function f() {
function localFunction() {
}
}
var globalVar = ‚a string‛;
function g() {
globalVar = ‚a string‛;
}
function g() {
window.globalVar = ‚a string‛;
}
window.window.window.window === window;
7. Why?
index.html:
<html><body>
<button onclick=‚makeCoffee()‛>Make Coffee</button>
<script src=‚coffeeFunctions.js‛></script>
<script>
function makeCoffee() {
var sugars = askAboutSugar();
var milk = askAboutMilk();
prepareMug(sugars, milk);
while (!groundsAreEmpty) requestUserEmptyGrounds();
while (!dripTrayIsEmpty) requestUserEmptyTray();
grindBeans();
putGroundsInFilter();
heatWater();
while (!waterHotEnough()) wait();
pourCoffeeInMug();
serveMugToUser();
};
</script>
</body></html>
coffeeFunctions.js:
function askAboutSugar() { … }
function askAboutMilk() { … }
function prepareMug(sugar, milk) { … }
function requestuserEmptyGrounds() { … }
function requestuserEmptyTray() { … }
function grindBeans() { … }
function putGroundsInFilter() { … }
function heatWater() { … }
function waterHotEnough() { … }
function pourCoffeeInMug() { … }
function serveMugToUser() { … }
8. Why?
index.html:
<html><body>
<button onclick=‚makeCoffee()‛>Make Coffee</button>
<script src=‚coffeeFunctions.js‛></script>
<script>
function makeCoffee() {
var sugars = askAboutSugar();
var milk = askAboutMilk();
prepareMug(sugars, milk);
while (!groundsAreEmpty) requestUserEmptyGrounds();
while (!dripTrayIsEmpty) requestUserEmptyTray();
grindBeans();
putGroundsInFilter();
heatWater();
while (!waterHotEnough()) wait();
pourCoffeeInMug();
serveMugToUser();
};
</script>
</body></html>
coffeeFunctions.js:
function askAboutSugar() { … }
function askAboutMilk() { … }
function prepareMug(sugar, milk) { … }
function requestuserEmptyGrounds() { … }
function requestuserEmptyTray() { … }
function grindBeans() { … }
function putGroundsInFilter() { … }
function heatWater() { … }
function waterHotEnough() { … }
function pourCoffeeInMug() {
stopHeatingWater();
openCoffeeTap();
pourWaterThroughFilter();
}
function serveMugToUser() { … }
23. IIFE Module Benefits
Code internals are encapsulated
Dependencies are explicitly named
Code is reusable (in contexts where the dependencies are already
available)
24. IIFE Module Problems
Global state has to be used to store each module exported from
an IIFE module
Namespacing requires manual initialization and management
Module loading and ordering still have to be managed manually
25. Asynchronous Module
Definitions (AMD)
define([‚lib/jquery‛, ‚lib/knockout‛, ‚coffeeMachine/grinder‛],
function ($, ko, coffeeGrinder) {
[… make coffee or build some private state or something …]
return {
‚doSomethingCoffeeRelated‛ : coffeeMakingFunction,
‚usefulNumber‛ : 4,
};
}
);
26. Asynchronous Module
Definitions (AMD)
define([‚lib/jquery‛, ‚lib/knockout‛, ‚coffeeMachine/grinder‛],
function ($, ko, coffeeGrinder) {
[… make coffee or build some private state or something …]
return {
‚doSomethingCoffeeRelated‛ : coffeeMakingFunction,
‚usefulNumber‛ : 4,
};
}
);
27. Asynchronous Module
Definitions (AMD)
define([‚lib/jquery‛, ‚lib/knockout‛, ‚coffeeMachine/grinder‛],
function ($, ko, coffeeGrinder) {
[… make coffee or build some private state or something …]
return {
‚doSomethingCoffeeRelated‛ : coffeeMakingFunction,
‚usefulNumber‛ : 4,
};
}
);
28. Asynchronous Module
Definitions (AMD)
define([‚lib/jquery‛, ‚lib/knockout‛, ‚coffeeMachine/grinder‛],
function ($, ko, coffeeGrinder) {
[… make coffee or build some private state or something …]
return {
‚doSomethingCoffeeRelated‛ : coffeeMakingFunction,
‚usefulNumber‛ : 4,
};
}
);
29. Asynchronous Module
Definitions (AMD)
define([‚lib/jquery‛, ‚lib/knockout‛, ‚coffeeMachine/grinder‛],
function ($, ko, coffeeGrinder) {
[… make coffee or build some private state or something …]
return {
‚doSomethingCoffeeRelated‛ : coffeeMakingFunction,
‚usefulNumber‛ : 4,
};
}
);
33. AMD Benefits
Code internals are encapsulated, with explicitly exposed
interfaces
Code is reusable as long as paths match or are aliased
Dependencies are explicitly named
Dependency loading is asynchronous, and can be done in
parallel
Implemented in vanilla JavaScript only; no fundamental new
semantics
34. AMD Problems
Lots of boilerplate (for JavaScript)
Lots of complexity
Can’t handle circular dependencies
Can result in code that requires many HTTP requests to pull down its
large dependency network (solvable with R.js or similar)
35. CommonJS Modules
var $ = require(‚jquery‛);
var coffeeGrinder = require(‚./coffeeGrinder‛);
var niceBeans = require(‚./coffeeBeans‛).NICE_BEANS;
[… code to do something tenuously coffee related …]
exports.doSomethingCoffeeRelated = function () { … };
exports.usefulNumber = 4;
36. CommonJS Modules
var $ = require(‚jquery‛);
var coffeeGrinder = require(‚./coffeeGrinder‛);
var niceBeans = require(‚./coffeeBeans‛).NICE_BEANS;
[… code to do something tenuously coffee related …]
exports.doSomethingCoffeeRelated = function () { … };
exports.usefulNumber = 4;
37. CommonJS Modules
var $ = require(‚jquery‛);
var CoffeeGrinder = require(‚./coffeeGrinder‛).CoffeeGrinder;
var niceBeans = require(‚./coffeeBeans‛).NICE_BEANS;
[… code to do something tenuously coffee related …]
exports.doSomethingCoffeeRelated = function () { … };
exports.usefulNumber = 4;
39. CommonJS Runners
Various non-browser platforms
Node.JS, CouchDB, Narwhal, XULJet
The native environment for CommonJS modules
Synchronous loading makes perfect sense server-side
Closer model to non-browser scripting languages
Browserify
Require.js
40. CommonJS Runners
Various non-browser platforms
Browserify
CommonJS modules for the browser
Build tool that takes CommonJS modules and compiles
the whole app into a single script file
Lets node.js modules work directly in a browser
Require.js
41. CommonJS Runners
Various non-browser platforms
Browserify
Require.js
Primarily an AMD script loader
Can support CommonJS style modules, hackily, with:
define(function(require, exports) {
var beanTypes = require(‚coffeeMachine/beanTypes‛);
exports.favouriteBeanType = beanTypes[0];
});
42. CommonJS Benefits
Code internals are encapsulated
Dependencies are explicitly named
Code is easily reusable
Simple clean syntax and conceptual model
Basically no boilerplate
Handles circular references better than AMD
43. CommonJS Problems
Lots of magic involved
Doesn’t follow standard JavaScript conventions
No consideration of environment where loads are expensive
Ignores JavaScript’s inherent asynchronicity
Dependencies aren’t necessarily all obvious upfront
44. ES6 Modules
module ‚aCoffeeComponent‛ {
import $ from ‘jquery’;
import { NICE_BEANS as niceBeans } from ‚beanTypes‛;
import ‘coffeeMachine/coffeeGrinder’ as grinder;
export default function doSomethingCoffeeRelated() { … };
export var usefulNumber = 4;
}
45. ES6 Modules
module ‚aCoffeeComponent‛ {
import $ from ‘jquery’;
import { NICE_BEANS as niceBeans } from ‚beanTypes‛;
import ‘coffeeMachine/coffeeGrinder’ as grinder;
export default function doSomethingCoffeeRelated() { … };
export var usefulNumber = 4;
}
46. ES6 Modules
module ‚aCoffeeComponent‛ {
import $ from ‘jquery’;
import { NICE_BEANS as niceBeans } from ‚beanTypes‛;
import ‘coffeeMachine/coffeeGrinder’ as grinder;
export default function doSomethingCoffeeRelated() { … };
export var usefulNumber = 4;
}
47. ES6 Modules
module ‚aCoffeeComponent‛ {
import $ from ‚jquery‛;
import { NICE_BEANS as niceBeans } from ‚beanTypes‛;
import ‘coffeeMachine/coffeeGrinder’ as grinder;
export default function doSomethingCoffeeRelated() { … };
export var usefulNumber = 4;
}
48. ES6 Module Benefits
Likely to be extremely well supported everywhere, eventually
More granular & powerful module import controls
New syntax, but otherwise fairly true to existing JS semantics
Fairly low on boilerplate
Handles circular references even better
Similar to other language concepts & syntax
Modules can be declared either inline, or nested, or externally
49. ES6 Module Problems
Currently supported effectively nowhere
Not even final in the spec yet
Quite a lot of genuinely new syntax
import * is included, but is frowned upon in every other language
Powerful, but thereby comparatively quite complicated
50. Which one do I use?
IIFE:
For tiny projects
For trivial
compatibility
AMD:
For most serious
browser-based
projects
For a no-build
pure-JS solution
If you need to
depend on non-JS
content/events
CommonJS:
For anything
outside a browser
environment
For anything in a
browser where
you might want
Node modules
ES6:
If you yearn for
the extremely
bleeding edge
And you live way
in the future
where it has real
support
51. Thank you
Tim Perry
Tech Lead & Open-Source Champion at Softwire
@pimterry / github.com/pimterry / tim-perry.co.uk