JavaScript Modules
Join the DZone community and get the full member experience.
Join For Free
one of the first challenges developers new to javascript who are
building large applications will have to face is how to go about
organizing their code. most start by embedding hundreds of lines of code
between a <script> tag which works but quickly turns
into a mess. the difficultly is that javascript doesn’t offer any
obvious help with organizing our code. literally where c# has using, java has import - javascript
has nothing. this has forced javascript authors to experiment with
different conventions and to use the language we do have to create
practical ways of organizing large javascript applications.
the patterns and tools and practices that will form the foundation of modern javascript are going to have to come from outside implementations of the language itself
the module pattern
one of the most widely used approaches to solve this problem is known
as the module pattern. i’ve attempted to explain a basic example below
and talk about some of it’s properties. for a much better description
and a fantastic run down of different approaches take a look at ben
cherry’s post –
javascript module pattern: in-depth
.
(function(lab49) { function privateadder(n1, n2) { return n1 + n2; } lab49.add = function(n1, n2) { return privateadder(n1); }; })(window.lab49 = window.lab49 || {});
in the above example we’ve used a number of basic features from the
language to create constructs like what we see in languages like c# and
java.
isolation
you’ll notice that the code is wrapped inside a function which is
invoked immediately (check the last line). by default in the browser
javascript files are evaluated in the global scope so anything we
declared inside our file would be available everywhere. imagine if in
lib1.js we had a var name = '...' statement then in lib2.js we had another var name = '...'
statement. the second var statement would replace the value of the
first – not good. however as javascript has function scoping, in the
above example everything is declared in it’s own scope away from the
global. this means anything in this function will be isolated from
whatever else is going on in the system.
namespacing
in the last line you’ll notice that we’re assigning window.lab49
to either itself or to an empty object literal. it looks a bit odd but
let’s walk through an imaginary system where we have a number of js
files all using the above function wrapper.
the first file to get included will evaluate that or statement and
find that the left hand side is undefined. this is a falsely value so
the or statement will go ahead and evaluate the right hand side, in this
case an empty object literal. the or statement is actually an
expression that will return it’s result and go ahead and assign it to
the global window.lab49.
now the next file to use this pattern will get to the or statement and find that window.lab49
is now an instance of an object – a truthy value. the or statement will
short circuit and return this value that is immediately assigned to
itself – effectively doing nothing.
the result of this is that the first file in will create our lab49
namespace (just a javascript object) and every subsequent file using
this construct will just reuse the existing instance.
private state
as we just talked about due to being inside a function, everything
declared inside it is in the scope of that function and not the global
scope. this is great to isolate our code but it also has the effect that
no one could call it. pretty useless.
as we also just talked about we’re creating a window.lab49 object to
effectively namespace our content. this lab49 variable is available
globally as it’s attached to the window object. to expose things outside
of our module, publically you may say, all we need to do attach values
to that global variable. much like we’re doing with our add function in
the above example. now outside of our module our add function can be
called with lab49.add(2, 2).
as another result of declaring our values inside of this function, if
a value isn’t explicitly exposed by attaching it to our global
namespace or something outside of the module there is no way for
external code to reach it. in practice, we’ve just created some private
values.
commonjs modules
commonjs is a group primarily made up of authors of server-side
javascript runtimes who have attempted to standardize exposing and
accessing modules. it’s worth noting however that their proposed module
system is not a standard from the same group that creates the javascript
standard so it’s become more of an informal convention between the
authors of server-side javascript runtimes.
i generally support the commonjs idea, but let’s be clear: it’s hardly a specification handed down by the gods (like es5); it’s just some people discussing ideas on a mailing list. most of these ideas are without actual implementations.
- ryan dahl , creator of node.js
the core of the
modules specification
is relatively straight forward. modules are evaluated in their own context and have a global exports variable made available to them. this exports
variable is just a plain old javascript object which you can attach
things too, similar to the namespace object we demonstrated above. to
access a module you call a global require function and give
an identifier for the package you are requesting. this then evaluates
the module and returns whatever was attached to the exports. this module will then be cached for subsequent require calls.
// calculator.js exports.add = function(n1, n2) { }; // app.js var calculator = require('./calculator'); calculator.add(2, 2);
if you’ve ever played with node.js you’ll probably find the above
familiar. the way that node implements commonjs modules is surprisingly
easy, looking at a module inside
node-inspector
(a node debugger) will show its content wrapped inside a function that
is being passed values for exports and require. very similar to the hand
rolled modules we showed above.
there’s a couple of node projects (
stitch
and
browserify
)
which bring commonjs modules to the browser. a server-side component
will bundle these individual module js files into a single js file with a
generated module wrapper around them.
commonjs was mainly designed for server-side javascript runtimes and
due to that there’s a couple of properties which can make them difficult
for organization of client-side code in the browser.
- require must return immediately – this works great when you already have all the content but makes it difficult to use a script loader to download the script asynchronously.
- one module per file – to combine commonjs modules they need to be wrapped in a function and then organized in some fashion. this makes them difficult to use without some server component like the ones mentioned above and in many environments (asp.net, java) these don’t yet exist.
asynchronous module definition
the asynchronous module definition (commonly known as amd) has been designed as a module format suitable for the browser. it started life as a proposal from the commonjs group but has since moved onto github and is now accompanied by a suite of tests to verify compliance to the amd api for module system authors.
the core of amd is the define function. the most common way to call define accepts three parameters – the name of the module (meaning that it’s no longer tied to the name of the file), an array of module identifiers that this module depends on, and a factory function which will return the definition of the module. (there are other ways to call define – check out the amd wiki for full details).
define('calculator', ['adder'], function(adder) { return { add: function(n1, n2) { return adder.add(n1, n2); } }; });
because of this module definition is wrapped in the define call it
means you can happily have multiple modules inside a single js file.
also as the module loader has control over when the define module
factory function is invoked it can resolve the dependencies in its own
time – handy if those modules have to first be downloaded
asynchronously.
a significant effort has been made to remain compatible with the
original commonjs module proposal. there is special behavior for using require and exports within a module factory function meaning that traditional commonjs modules can be dropped right in.
amd looks to be becoming a very popular way to organize client-side
javascript applications. whether it be through module resource loaders
like
requirejs
or
curl.js
, or javascript applications that have recently embraced amd like
dojo
.
does this mean javascript sucks?
the lack of any language level constructs for organization of code
into modules can be quite jarring for developers coming from other
languages. however as this deficiency forced javascript developers to
come up with their own patterns for how modules were structured we’ve
been able to iterate and improve as javascript applications have
evolved. follow the
tagneto
blog for some insight into this.
imagine if this type of functionality had been included in the
language 10 years ago. it’s unlikely they would have imagined the
requirements for running large javascript applications on the server,
loading resources asynchronously in the browser, or including resources
like
text templates
that loaders like requirejs are able to do.
modules are being considered as a language level feature for
harmony/ecmascript 6
.
thanks to the thought and hard work of authors of module systems over
the past few years, it’s much more likely that what we end up getting
will be suitable for how modern javascript applications are built.
source: http://blog.davidpadbury.com/2011/08/21/javascript-modules/
Opinions expressed by DZone contributors are their own.
Comments