Dave Herman

7 downloads 306 Views 11MB Size Report
Lazy application loading. Live update/hot-patching. Stratified ... Programmers are asking for modularity constructs. Jav
JavaScript needs modules! Dave Herman

The module pattern

$ = ...; // global (shared) var x, y, z; // local (private) ...

The module pattern (function() { $ = ...; // global (shared) var x, y, z; // local (private) ... })()

The module pattern (function() { var old = $; $ = ...; // global (shared) var x, y, z; // local (private) ... })()

The module pattern (function() { var old = $; $ = ...; // global (shared) var x, y, z; // local (private) ... function restore() { $ = old; } })()

Patterns are workarounds module jQuery { export var $ = ...; var x, y, z; ... }

Patterns are workarounds module jQuery { export var $ = ...; var x, y, z; ... } import jQuery.$; ... $(‘#foo’).css(‘background’, ‘blue’);

“Simple modules” Overriding principle: K.I.S.S. Began work last fall, revised over recent months Active discussions in TC39, es-discuss Target: ECMA-262, Edition k (Harmony) Next stage: implement in Narcissus

Gratitude for CommonJS

Raised interest, awareness Good design within confines of existing language But we can and should improve JavaScript

JavaScript needs modules!

Towards a better JavaScript with: 1. Lightweight code organization 2. Lexical scope in a dynamic environment 3. Dynamic loading and isolation

Use case: scripts grow

var DROPPED = 0, DRAGGING = 1; function Widget() { this.state = DROPPED; ... }

Use case: scripts grow module UI { var DROPPED = 0, DRAGGING = 1; function Widget() { this.state = DROPPED; ... } }

Use case: scripts grow only external access point module UI { var DROPPED = 0, DRAGGING = 1; function Widget() { this.state = DROPPED; ... } } everything is private by default

Use case: scripts grow module UI { var DROPPED = 0, DRAGGING = 1; export function Widget() { this.state = DROPPED; ... } }

Use case: scripts grow module UI { var DROPPED = 0, DRAGGING = 1; export function Widget() { this.state = DROPPED; ... } } ... var widget = new UI.Widget();

Use case: cyclic dependencies

function even(n) { ... odd(n - 1) ... }

function odd(n) { ... even(n - 1) ... }

Use case: cyclic dependencies module Even { import Odd.odd; export function even(n) { ... odd(n - 1) ... } } module Odd { import Even.even; export function odd(n) { ... even(n - 1) ... } }

Keep the flow of development

module-ifying code should be trivial Changes to clients should be trivial No ripple effects (e.g., C++ const) Preserve existing program organization

JavaScript needs modules!

Towards a better JavaScript with: 1. Lightweight code organization 2. Lexical scope in a dynamic environment 3. Dynamic loading and isolation

The global object undeclared App = (function() { var frames = ... for (i = 0; i < frames.length; i++) { UI.initFrame(frames[i]) } ... })()

The global object undeclared App = (function() { var frames = ... for (i = 0; i < frames.length; i++) { UI.initFrame(frames[i]) } ... })()

undeclared

UI = (function() { function initFrame(frame) { for (i = 0; i < frame.menus.length; i++) { initMenu(frame.menus[i]) } } ... })()

The global object undeclared App = (function() { var frames = ... for (i = 0; i < frames.length; i++) { UI.initFrame(frames[i]) } ... })()



undeclared

UI = (function() { function initFrame(frame) { for (i = 0; i < frame.menus.length; i++) { initMenu(frame.menus[i]) } } ... })()

The global object

Shouldn’t have to audit code for globals. Variable references shouldn’t cause runtime errors. Variable lookup should be fast. Global variables shouldn’t be the default.

Harmony: opt in to a better JS

Lexical scope all the way down

No more global object ES5 strict: no more with ES5 strict: protected eval All variables are bound statically

Declaring modules compiletime load module jQuery = load “jquery.js”; module Collections { ... } module A = B;

Declaring external modules Client of jquery.js:

client names the module

module jQuery = load “jquery.js”; jQuery.$(‘#foo’).css(...);

Contents of jquery.js: export var $ = ...; var x, y, z; ...

Rationale: the web is big Nightmarish visions of reverse-DNS run amok: import com.example.util.collections.maps.HashMap;

DNS isn’t permanent; names change Trying to get away from global namespace! Let the client name it

Rationale: the web is big jquery.js: module Util { ... }

ext.js: module Util { ... }

Nested modules client gets to name libraries locally module jQuery = load “jquery.js”; module Ext = load “ext.js”; ... jQuery.Util.grep(...) ... ... Ext.Util.capitalize(...) ... libraries name members locally, without contention

Lexical scope One of “the good parts” ;) No dynamic variable lookup Early fat-finger errors Reduced global pollution hazards Code locally; share globally

JavaScript needs modules!

Towards a better JavaScript with: 1. Lightweight code organization 2. Lexical scope in a dynamic environment 3. Dynamic loading and isolation

Second-class ≠ purely static Module instances can be reflected as objects: module M { export foo, bar; ... } ... function inspect(obj) { for (var key in obj) { log(key + ‘=’ + obj[key]); } } inspect(M);

Module loaders Create fresh, isolated module contexts: var ml = new ModuleLoader();

Load modules in new context: ml.loadModule(“TestHarness”, “test-harness.js”); ml.loadModule(“Tests”, “tests.js”); try { ml.evalScript(“Tests.run()”); } catch (e) { alert(“tests failed”); }

Module loaders Alternative load behavior via a hook: var ml = new ModuleLoader(function(url, response) { var realURL = lookupTable[url]; if (realURL) response.accept(realURL); else response.reject(); });

Loading code in new context: ml.loadModule(“Sketchy”, “sketchy.js”);

Use cases Feature detection Lazy application loading Live update/hot-patching Stratified environments (e.g., test suites, Bespin) Managing untrusted code (e.g., XSS, ads)

Takeaways

Programmers are asking for modularity constructs JavaScript longs to be lexically scoped Static modules fit with the dynamic web

Takeaways

Make it easier to share code, and people will share more.