Module Pattern
Revealing Module Pattern
AMD
commonJS
ES6
<script src="/jquery.js"></script>
<script>
var $element = $( 'button' );
$element.css({
opacity: 1
});
$button.click( function( event ) {
$element.css({
opacity: 0
});
});
</script>
<script>
var $element = $( 'header' );
$element.css({
background: 'red'
});
</script>Simple to integrate
Still able to split into separate files
Leaking globals
No encapsulation
No interoperability
<script src="/jquery.js"></script>
<script>
var module = (function() {
// Private
var foo = 'foo';
function privateMethod() {
...
}
return {
getFoo: function() {
return foo;
},
publicMethod: function() {
...
}
};
})();
// Error - private methods
console.log( module.foo );
module.privateMethod();
// Accessing public API
console.log( module.getFoo() );
module.publicMethod();
</script>
<script src="/jquery.js"></script>
<script>
var module = (function( $ ) {
// Private
var $foo = $( '#foo' );
function privateMethod() {
$foo.css({ background: '#d4d6d7' });
}
return {
publicMethod: function() {
privateMethod();
}
};
})( jQuery );
// Error - private method
console.log( $foo );
module.privateMethod();
// Accessing public API
module.publicMethod();
</script>
<script>
(function( root ) {
var privateFoo = 'foo';
var module = {
create: function() {
return {
publicMethod: function() {
module.privateMethod();
console.log( privateFoo );
}
}
},
privateMethod: function() {...}
}
root.module = module.create();
})( this );
</script>
Encapsulation
Ability to 'reveal' an API
Limits global leak
Patches and changes, i.e. from private to public, can become problematic as the way in which they are referenced changes
Still leaks globals, which can conflict
Data privacy
Late declaration of additional methods (or extensions) to the module wont have access to private members
Unit testing private variables is impossible
<script src="/jquery.js"></script>
<script>
var module = (function() {
var foo = 'foo';
function bar() {...};
// Public API
return {
variable: foo,
publicMethod: bar
};
})();
console.log( module.variable );
module.publicMethod();
</script>
Syntactically more consistent than standard module pattern
Clear and concise public API declaration
A late patch to the module object will fail if functions reference each other as any patch will only be applied to the publicly exposed functions, not those used by the module directly.
In short, modules become slightly more fragile.
// script.js
!function() {
define( 'myAwesomeModule',
[
'jQuery',
'lodash'
],
function( $, _ ) {
return {
foo: 'foo',
bar: function() { ... }
};
}
);
require( [ 'myAwesomeModule' ], function( module ) {
module.bar();
console.log( module.foo );
});
}();
Defines a module structure
Dependency management
Proper encapsulation and preservation of global, thus eliminating naming conflicts
Highly flexible
Encourages asynchronous loading and allows lazy loading of scripts
Fair amount of boilerplate
Its flexibility means it can be confusing or overly verbose
Is asynchronous script loading really as useful as you might think?
// script.js
var $ = require( 'jQuery' );
var _ = require( 'lodash' );
module.exports = {
foo: 'foo',
bar: function() {...}
};
Many similar pros to AMD, such as encapsulation and clear definition
Super simple
Simplicity
Requires a build step to make sense in browser-land without destroying global
// module.js
var privateFoo = 'foo';
export default class Superman extends Man {
constructor( name ) {
this.name = name;
}
get name() {
return this.name;
}
say() {
super();
console.log( 'Hello from', this.name );
}
}
// app.js
import Superman from 'module';
var clark = new Superman( 'Clark' );
Proper module system
Will likely be available to use as part of a <module> tag
Simple CommonJS format
Fairly gnarly build steps involved
Most libraries currently dont support ES6 modules, however, there are build step work arounds which will attempt to work out the type of module being imported and load it in a sensible manner. This allows ES6, CommonJS and AMD to be used alongside each other.