Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

24
11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony ² addyo… 1/24 www.readability.com/articles/ummplmts?legacy_bookmarklet=1# DGG\RVPDQL.FRP Writing Modular JavaScript With AMD, CommonJS & ES Harmon\ TZHHW Modularit\ The Importance Of Decoupling Your Application WKHQ ZH VD\ DQ DSSOLFDWLRQ LV modular, ZH JHQHUDOO\ PHDQ LW'V FRPSRVHG RI D VHW RI KLJKO\ GHFRXSOHG, GLVWLQFW SLHFHV RI IXQFWLRQDOLW\ VWRUHG LQ PRGXOHV. AV \RX SUREDEO\ NQRZ, ORRVH FRXSOLQJ IDFLOLWDWHV HDVLHU PDLQWDLQDELOLW\ RI DSSV E\ UHPRYLQJ dependencieV ZKHUH SRVVLEOH. WKHQ WKLV LV LPSOHPHQWHG HIILFLHQWO\, LWV TXLWH HDV\ WR VHH KRZ FKDQJHV WR RQH SDUW RI D V\VWHP PD\ DIIHFW DQRWKHU. UQOLNH VRPH PRUH WUDGLWLRQDO SURJUDPPLQJ ODQJXDJHV KRZHYHU, WKH FXUUHQW LWHUDWLRQ RI JDYDSFUL SW (ECMA-262) GRHVQ'W SURYLGH GHYHORSHUV ZLWK WKH PHDQV WR LPSRUW VXFK PRGXOHV RI FRGH LQ D FOHDQ, RUJDQL]HG PDQQHU. IW'V RQH RI WKH FRQFHUQV ZLWK VSHFLILFDWLRQV WKDW KDYHQ'W UHTXLUHG JUHDW WKRXJKW XQWLO PRUH UHFHQW \HDUV ZKHUH WKH QHHG IRU PRUH RUJDQL]HG JDYDSFUL SW DSSOLFDWLRQV EHFDPH DSSDUHQW. IQVWHDG, GHYHORSHUV DW SUHVHQW DUH OHIW WR IDOO EDFN RQ YDULDWLRQV RI WKH PRGXOH RU REMHFW OLWHUDO SDWWHUQV. WL WK PDQ\ RI WKHVH, PRGXOH VFULSWV DUH VWUXQJ WRJHWKHU LQ WKH DOM ZLWK QDPHVSDFHV EHLQJ GHVFULEHG E\ D VLQJOH JOREDO REMHFW ZKHUH LW'V VWLOO SRVVLEOH WR LQFXU QDPLQJ FROOLVLRQV LQ \RXU DUFKLWHFWXUH. TKHUH'V DOVR QR FOHDQ ZD\ WR KDQGOH GHSHQGHQF\ PDQDJHPHQW ZLWKRXW VRPH PDQXDO HIIRUW RU WKLUG SDUW\ WRROV. WKLOVW QDWLYH VROXWLRQV WR WKHVH SUREOHPV ZLOO EH DUULYLQJ LQ ES HDUPRQ\, WKH JRRG QHZV LV WKDW ZULWLQJ PRGXODU JDYDSFUL SW KDV QHYHU EHHQ HDVLHU DQG \RX FDQ VWDUW GRLQJ LW WRGD\. IQ WKLV DUWLFOH, ZH'UH JRLQJ WR ORRN DW WKUHH IRUPDWV IRU ZULWLQJ PRGXODU JDYDSFUL SW: AMD, CommonJS DQG SURSRVDOV IRU WKH QH[W YHUVLRQ RI JDYDSFUL SW, Harmon\. Prelude A Note On Script Loaders IW'V GL IILFXOW WR GLVFXVV AMD DQG CRPPRQJS PRGXOHV ZLWKRXW WDONLQJ DERXW WKH HOHSKDQW LQ WKH URRP - VFULSW ORDGHUV. AW SUHVHQW, VFULSW ORDGLQJ LV D PHDQV WR D JRDO, WKDW JRDO EHLQJ PRGXODU JDYDSFUL SW WKDW FDQ EH XVHG LQ DSSOLFDWLRQV WRGD\ - IRU WKLV, XVH RI D FRPSDWLEOH VFULSW ORDGHU LV XQIRUWXQDWHO\ QHFHVVDU\. IQ RUGHU WR JHW WKH PRVW RXW RI WKLV DUWLFOH, I UHFRPPHQG JDLQLQJ D basic understanding RI KRZ SRSXODU VFULSW ORDGLQJ WRROV ZRUN VR WKH H[SODQDWLRQV RI PRGXOH IRUPDWV PDNH VHQVH LQ FRQWH[W. TKHUH DUH D QXPEHU RI JUHDW ORDGHUV IRU KDQGOLQJ PRGXOH ORDGLQJ LQ WKH AMD DQG CJS IRUPDWV, EXW P\ SHUVRQDO SUHIHUHQFHV DUH RHTXLUHJS DQG FXUO.MV. CRPSOHWH WXWRULDOV RQ WKHVH WRROV DUH RXWVLGH WKH VFRSH RI

Transcript of Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

Page 1: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

1/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

addyosmani.com

Writing Modular JavaScript With AMD,CommonJS & ES Harmony

Tweet

Modularity The Importance Of Decoupling Your Application

When we say an application is modular, we generally mean it's composed of a set of highly decoupled,

distinct pieces of functionality stored in modules. As you probably know, loose coupling facilitates easier

maintainability of apps by removing dependencies where possible. When this is implemented efficiently,

its quite easy to see how changes to one part of a system may affect another.

Unlike some more traditional programming languages however, the current iteration of JavaScript

(ECMA-262) doesn't provide developers with the means to import such modules of code in a clean,

organized manner. It's one of the concerns with specifications that haven't required great thought until

more recent years where the need for more organized JavaScript applications became apparent.

Instead, developers at present are left to fall back on variations of the module or object literal patterns.

With many of these, module scripts are strung together in the DOM with namespaces being described by

a single global object where it's still possible to incur naming collisions in your architecture. There's also

no clean way to handle dependency management without some manual effort or third party tools.

Whilst native solutions to these problems will be arriving in ES Harmony, the good news is that writing

modular JavaScript has never been easier and you can start doing it today.

In this article, we're going to look at three formats for writing modular JavaScript: AMD, CommonJS

and proposals for the next version of JavaScript, Harmony.

Prelude A Note On Script Loaders

It's difficult to discuss AMD and CommonJS modules without talking about the elephant in the room -

script loaders. At present, script loading is a means to a goal, that goal being modular JavaScript that

can be used in applications today - for this, use of a compatible script loader is unfortunately necessary.

In order to get the most out of this article, I recommend gaining a basic understanding of how

popular script loading tools work so the explanations of module formats make sense in context.

There are a number of great loaders for handling module loading in the AMD and CJS formats, but my

personal preferences are RequireJS and curl.js. Complete tutorials on these tools are outside the scope of

Page 2: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

2/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

this article, but I can recommend reading John Hann's post about curl.js and James Burke's

ScriptJunkie post on RequireJS.

From a production perspective, the use of optimization tools (like the RequireJS optimizer) to

concatenate scripts is recommended for deployment when working with such modules. Interestingly,

with the Almond AMD shim, RequireJS doesn't need to be rolled in the deployed site and what you

might consider a script loader can be easily shifted outside of development.

That said, James Burke would probably say that being able to dynamically load scripts after page load

still has its use cases and RequireJS can assist with this too. With these notes in mind, let's get started.

AMD A Format For Writing Modular JavaScript In The Browser

The overall goal for the AMD (Asynchronous Module Definition) format is to provide a solution for

modular JavaScript that developers can use today. It was born out of Dojo's real world experience using

XHR+eval and proponents of this format wanted to avoid any future solutions suffering from the

weaknesses of those in the past.

The AMD module format itself is a proposal for defining modules where both the module and

dependencies can be asynchronously loaded. It has a number of distinct advantages including being

both asynchronous and highly flexible by nature which removes the tight coupling one might

commonly find between code and module identity. Many developers enjoy using it and one could

consider it a reliable stepping stone towards the module system proposed for ES Harmony.

AMD began as a draft specification for a module format on the CommonJS list but as it wasn't able to

reach full concensus, further development of the format moved to the amdjs group.

Today it's embraced by projects including Dojo (1.7), MooTools (2.0), Firebug (1.8) and even jQuery

(1.7). Although the term CommonJS AMD format has been seen in the wild on occasion, it's best to

refer to it as just AMD or Async Module support as not all participants on the CJS list wished to pursue

it.

Note: There was a time when the proposal was referred to as Modules Transport/C, however as the spec

wasn't geared for transporting existing CJS modules, but rather, for defining modules it made more

sense to opt for the AMD naming convention.

Getting Started With Modules

The two key concepts you need to be aware of here are the idea of a d e f i n e method for facilitating

module definition and a r e q u i r e method for handling dependency loading. define is used to define

named or unnamed modules based on the proposal using the following signature:

Page 3: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

3/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

d e f i n e (

m o d u l e _ i d / * o p t i o n a l * / ,

[ d e p e n d e n c i e s ] / * o p t i o n a l * / ,

d e f i n i t i o n f u n c t i o n / * f u n c t i o n f o r i n s t a n t i a t i n g t h e m o d u l e o r o b j e c t * /

) ;

As you can tell by the inline comments, the m o d u l e _ i d is an optional argument which is typically only

required when non-AMD concatenation tools are being used (there may be some other edge cases where

it's useful too). When this argument is left out, we call the module anonymous.

When working with anonymous modules, the idea of a module's identity is DRY, making it trivial to

avoid duplication of filenames and code. Because the code is more portable, it can be easily moved to

other locations (or around the file-system) without needing to alter the code itself or change its ID. The

m o d u l e _ i d is equivalent to folder paths in simple packages and when not used in packages. Developers

can also run the same code on multiple environments just by using an AMD optimizer that works with a

CommonJS environment such as r.js.

Back to the define signature, the dependencies argument represents an array of dependencies which are

required by the module you are defining and the third argument ('definition function') is a function

that's executed to instantiate your module. A barebone module could be defined as follows:

Understanding AMD: define()

/ / A m o d u l e _ i d ( m y M o d u l e ) i s u s e d h e r e f o r d e m o n s t r a t i o n p u r p o s e s o n l y

d e f i n e ( ' m y M o d u l e ' ,

[ ' f o o ' , ' b a r ' ] ,

/ / m o d u l e d e f i n i t i o n f u n c t i o n

/ / d e p e n d e n c i e s ( f o o a n d b a r ) a r e m a p p e d t o f u n c t i o n p a r a m e t e r s

f u n c t i o n ( f o o , b a r ) {

/ / r e t u r n a v a l u e t h a t d e f i n e s t h e m o d u l e e x p o r t

/ / ( i . e t h e f u n c t i o n a l i t y w e w a n t t o e x p o s e f o r c o n s u m p t i o n )

/ / c r e a t e y o u r m o d u l e h e r e

v a r m y M o d u l e = {

d o S t u f f : f u n c t i o n ( ) {

c o n s o l e . l o g ( ' Y a y ! S t u f f ' ) ;

}

}

Page 4: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

4/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

r e t u r n m y M o d u l e ;

} ) ;

/ / A n a l t e r n a t i v e e x a m p l e c o u l d b e . .

d e f i n e ( ' m y M o d u l e ' ,

[ ' m a t h ' , ' g r a p h ' ] ,

f u n c t i o n ( m a t h , g r a p h ) {

/ / N o t e t h a t t h i s i s a s l i g h t l y d i f f e r e n t p a t t e r n

/ / W i t h A M D , i t ' s p o s s i b l e t o d e f i n e m o d u l e s i n a f e w

/ / d i f f e r e n t w a y s d u e a s i t ' s r e l a t i v e l y f l e x i b l e w i t h

/ / c e r t a i n a s p e c t s o f t h e s y n t a x

r e t u r n {

p l o t : f u n c t i o n ( x , y ) {

r e t u r n g r a p h . d r a w P i e ( m a t h . r a n d o m G r i d ( x , y ) ) ;

}

}

} ;

} ) ;

require on the other hand is typically used to load code in a top-level JavaScript file or within a module

should you wish to dynamically fetch dependencies. An example of its usage is:

Understanding AMD: require()

/ / C o n s i d e r ' f o o ' a n d ' b a r ' a r e t w o e x t e r n a l m o d u l e s

/ / I n t h i s e x a m p l e , t h e ' e x p o r t s ' f r o m t h e t w o m o d u l e s l o a d e d a r e p a s s e d a s

/ / f u n c t i o n a r g u m e n t s t o t h e c a l l b a c k ( f o o a n d b a r )

/ / s o t h a t t h e y c a n s i m i l a r l y b e a c c e s s e d

r e q u i r e ( [ ' f o o ' , ' b a r ' ] , f u n c t i o n ( f o o , b a r ) {

/ / r e s t o f y o u r c o d e h e r e

f o o . d o S o m e t h i n g ( ) ;

} ) ;

Dynamically-loaded Dependencies

d e f i n e ( f u n c t i o n ( r e q u i r e ) {

v a r i s R e a d y = f a l s e , f o o b a r ;

Page 5: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

5/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

/ / n o t e t h e i n l i n e r e q u i r e w i t h i n o u r m o d u l e d e f i n i t i o n

r e q u i r e ( [ ' f o o ' , ' b a r ' ] , f u n c t i o n ( f o o , b a r ) {

i s R e a d y = t r u e ;

f o o b a r = f o o ( ) + b a r ( ) ;

} ) ;

/ / w e c a n s t i l l r e t u r n a m o d u l e

r e t u r n {

i s R e a d y : i s R e a d y ,

f o o b a r : f o o b a r

} ;

} ) ;

Understanding AMD: plugins

The following is an example of defining an AMD-compatible plugin:

/ / W i t h A M D , i t ' s p o s s i b l e t o l o a d i n a s s e t s o f a l m o s t a n y k i n d

/ / i n c l u d i n g t e x t - f i l e s a n d H T M L . T h i s e n a b l e s u s t o h a v e t e m p l a t e

/ / d e p e n d e n c i e s w h i c h c a n b e u s e d t o s k i n c o m p o n e n t s e i t h e r o n

/ / p a g e - l o a d o r d y n a m i c a l l y .

d e f i n e ( [ ' . / t e m p l a t e s ' , ' t e x t ! . / t e m p l a t e . m d ' , ' c s s ! . / t e m p l a t e . c s s ' ] ,

f u n c t i o n ( t e m p l a t e s , t e m p l a t e ) {

c o n s o l e . l o g ( t e m p l a t e s ) ;

/ / d o s o m e f u n t e m p l a t e s t u f f h e r e .

}

} ) ;

Note: Although css! is included for loading CSS dependencies in the above example, it's important to

remember that this approach has some caveats such as it not being fully possible to establish when the

CSS is fully loaded. Depending on how you approach your build, it may also result in CSS being included

as a dependency in the optimized file, so use CSS as a loaded dependency in such cases with caution.

Loading AMD Modules Using require.js

r e q u i r e ( [ ' a p p / m y M o d u l e ' ] ,

f u n c t i o n ( m y M o d u l e ) {

/ / s t a r t t h e m a i n m o d u l e w h i c h i n - t u r n

/ / l o a d s o t h e r m o d u l e s

Page 6: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

6/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

v a r m o d u l e = n e w m y M o d u l e ( ) ;

m o d u l e . d o S t u f f ( ) ;

} ) ;

Loading AMD Modules Using curl.js

c u r l ( [ ' a p p / m y M o d u l e . j s ' ] ,

f u n c t i o n ( m y M o d u l e ) {

/ / s t a r t t h e m a i n m o d u l e w h i c h i n - t u r n

/ / l o a d s o t h e r m o d u l e s

v a r m o d u l e = n e w m y M o d u l e ( ) ;

m o d u l e . d o S t u f f ( ) ;

} ) ;

Modules With Deferred Dependencies

/ / T h i s c o u l d b e c o m p a t i b l e w i t h j Q u e r y ' s D e f e r r e d i m p l e m e n t a t i o n ,

/ / f u t u r e s . j s ( s l i g h t l y d i f f e r e n t s y n t a x ) o r a n y o n e o f a n u m b e r

/ / o f o t h e r i m p l e m e n t a t i o n s

d e f i n e ( [ ' l i b / D e f e r r e d ' ] , f u n c t i o n ( D e f e r r e d ) {

v a r p r o m i s e = n e w D e f e r r e d ( ) ;

r e q u i r e ( [ ' l i b / t e m p l a t e s / ? i n d e x . h t m l ' , ' l i b / d a t a / ? s t a t s ' ] ,

f u n c t i o n ( t e m p l a t e , d a t a ) {

p r o m i s e . r e s o l v e ( { t e m p l a t e : t e m p l a t e , d a t a : d a t a } ) ;

}

) ;

r e t u r n p r o m i s e ;

} ) ;

Why Is AMD A Better Choice For Writing Modular JavaScript?

Provides a clear proposal for how to approach defining flexible modules.

Significantly cleaner than the present global namespace and < s c r i p t > tag solutions many of us

rely on. There's a clean way to declare stand-alone modules and dependencies they may have.

Module definitions are encapsulated, helping us to avoid pollution of the global namespace.

Works better than some alternative solutions (eg. CommonJS, which we'll be looking at shortly).

Doesn't have issues with cross-domain, local or debugging and doesn't have a reliance on server-

side tools to be used. Most AMD loaders support loading modules in the browser without a build

process.

Provides a 'transport' approach for including multiple modules in a single file. Other approaches like

CommonJS have yet to agree on a transport format.

Page 7: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

7/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

It's possible to lazy load scripts if this is needed.

AMD Modules With Dojo

Defining AMD-compatible modules using Dojo is fairly straight-forward. As per above, define any

module dependencies in an array as the first argument and provide a callback (factory) which will

execute the module once the dependencies have been loaded. e.g:

d e f i n e ( [ " d i j i t / T o o l t i p " ] , f u n c t i o n ( T o o l t i p ) {

/ / O u r d i j i t t o o l t i p i s n o w a v a i l a b l e f o r l o c a l u s e

n e w T o o l t i p ( . . . ) ;

} ) ;

Note the anonymous nature of the module which can now be both consumed by a Dojo asynchronous

loader, RequireJS or the standard dojo.require() module loader that you may be used to using.

For those wondering about module referencing, there are some interesting gotchas that are useful to

know here. Although the AMD-advocated way of referencing modules declares them in the dependency

list with a set of matching arguments, this isn't supported by the Dojo 1.6 build system - it really only

works for AMD-compliant loaders. e.g:

d e f i n e ( [ " d o j o / c o o k i e " , " d i j i t / T o o l t i p " ] , f u n c t i o n ( c o o k i e , T o o l t i p ) {

v a r c o o k i e V a l u e = c o o k i e ( " c o o k i e N a m e " ) ;

n e w T r e e ( . . . ) ;

} ) ;

This has many advances over nested namespacing as modules no longer need to directly reference

complete namespaces every time - all we require is the 'dojo/cookie' path in dependencies, which once

aliased to an argument, can be referenced by that variable. This removes the need to repeatedly type out

'dojo.' in your applications.

Note: Although Dojo 1.6 doesn't officially support user-based AMD modules (nor asynchronous

loading), it's possible to get this working with Dojo using a number of different script loaders. At present,

all Dojo core and Dijit modules have been transformed to the AMD syntax and improved overall AMD

support will likely land between 1.7 and 2.0.

The final gotcha to be aware of is that if you wish to continue using the Dojo build system or wish to

migrate older modules to this newer AMD-style, the following more verbose version enables easier

migration. Notice that dojo and dijit and referenced as dependencies too:

d e f i n e ( [ " d o j o " , " d i j i t " , " d o j o / c o o k i e " , " d i j i t / T o o l t i p " ] , f u n c t i o n ( d o j o , d i j i t ) {

v a r c o o k i e V a l u e = d o j o . c o o k i e ( " c o o k i e N a m e " ) ;

Page 8: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

8/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

n e w d i j i t . T o o l t i p ( . . . ) ;

} ) ;

AMD Module Design Patterns (Dojo)

If you've followed any of my previous posts on the benefits of design patterns, you'll know that they can

be highly effective in improving how we approach structuring solutions to common development

problems. John Hann recently gave an excellent presentation about AMD module design patterns

covering the Singleton, Decorator, Mediator and others. I highly recommend checking out his slides if

you get a chance.

Some samples of these patterns can be found below:

Decorator pattern:

/ / m y l i b / U p d a t a b l e O b s e r v a b l e : a d e c o r a t o r f o r d o j o / s t o r e / O b s e r v a b l e

d e f i n e ( [ ' d o j o ' , ' d o j o / s t o r e / O b s e r v a b l e ' ] , f u n c t i o n ( d o j o , O b s e r v a b l e ) {

r e t u r n f u n c t i o n U p d a t a b l e O b s e r v a b l e ( s t o r e ) {

v a r o b s e r v a b l e = d o j o . i s F u n c t i o n ( s t o r e . n o t i f y ) ? s t o r e :

n e w O b s e r v a b l e ( s t o r e ) ;

o b s e r v a b l e . u p d a t e d = f u n c t i o n ( o b j e c t ) {

d o j o . w h e n ( o b j e c t , f u n c t i o n ( i t e m O r A r r a y ) {

d o j o . f o r E a c h ( [ ] . c o n c a t ( i t e m O r A r r a y ) , t h i s . n o t i f y , t h i s ) ;

} ;

} ;

r e t u r n o b s e r v a b l e ; / / m a k e s ` n e w ` o p t i o n a l

} ;

} ) ;

/ / d e c o r a t o r c o n s u m e r

/ / a c o n s u m e r f o r m y l i b / U p d a t a b l e O b s e r v a b l e

d e f i n e ( [ ' m y l i b / U p d a t a b l e O b s e r v a b l e ' ] , f u n c t i o n ( m a k e U p d a t a b l e ) {

v a r o b s e r v a b l e , u p d a t a b l e , s o m e I t e m ;

/ / . . . h e r e b e c o d e t o g e t o r c r e a t e ` o b s e r v a b l e `

/ / . . . m a k e t h e o b s e r v a b l e s t o r e u p d a t a b l e

Page 9: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

9/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

u p d a t a b l e = m a k e U p d a t a b l e ( o b s e r v a b l e ) ; / / ` n e w ` i s o p t i o n a l !

/ / . . . l a t e r , w h e n a c o m e t d m e s s a g e a r r i v e s w i t h n e w d a t a i t e m

u p d a t a b l e . u p d a t e d ( u p d a t e d I t e m ) ;

} ) ;

Adapter pattern

/ / ' m y l i b / A r r a y ' a d a p t s ` e a c h ` f u n c t i o n t o m i m i c j Q u e r y ' s :

d e f i n e ( [ ' d o j o / _ b a s e / l a n g ' , ' d o j o / _ b a s e / a r r a y ' ] , f u n c t i o n ( l a n g , a r r a y ) {

r e t u r n l a n g . d e l e g a t e ( a r r a y , {

e a c h : f u n c t i o n ( a r r , l a m b d a ) {

a r r a y . f o r E a c h ( a r r , f u n c t i o n ( i t e m , i ) {

l a m b d a . c a l l ( i t e m , i , i t e m ) ; / / l i k e j Q u e r y ' s e a c h

} )

}

} ) ;

} ) ;

/ / a d a p t e r c o n s u m e r

/ / ' m y a p p / m y - m o d u l e ' :

d e f i n e ( [ ' m y l i b / A r r a y ' ] , f u n c t i o n ( a r r a y ) {

a r r a y . e a c h ( [ ' u n o ' , ' d o s ' , ' t r e s ' ] , f u n c t i o n ( i , e s p ) {

/ / h e r e , ` t h i s ` = = i t e m

} ) ;

} ) ;

AMD Modules With jQueryThe Basics

Unlike Dojo, jQuery really only comes with one file, however given the plugin-based nature of the

library, we can demonstrate how straight-forward it is to define an AMD module that uses it below.

d e f i n e ( [ ' j s / j q u e r y . j s ' , ' j s / j q u e r y . c o l o r . j s ' , ' j s / u n d e r s c o r e . j s ' ] ,

f u n c t i o n ( $ , c o l o r P l u g i n , _ ) {

/ / H e r e w e ' v e p a s s e d i n j Q u e r y , t h e c o l o r p l u g i n a n d U n d e r s c o r e

/ / N o n e o f t h e s e w i l l b e a c c e s s i b l e i n t h e g l o b a l s c o p e , b u t w e

/ / c a n e a s i l y r e f e r e n c e t h e m b e l o w .

Page 10: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

10/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

/ / P s e u d o - r a n d o m i z e a n a r r a y o f c o l o r s , s e l e c t i n g t h e f i r s t

/ / i t e m i n t h e s h u f f l e d a r r a y

v a r s h u f f l e C o l o r = _ . f i r s t ( _ . s h u f f l e ( [ ' # 6 6 6 ' , ' # 3 3 3 ' , ' # 1 1 1 ' ] ) ) ;

/ / A n i m a t e t h e b a c k g r o u n d - c o l o r o f a n y e l e m e n t s w i t h t h e c l a s s

/ / ' i t e m ' o n t h e p a g e u s i n g t h e s h u f f l e d c o l o r

$ ( ' . i t e m ' ) . a n i m a t e ( { ' b a c k g r o u n d C o l o r ' : s h u f f l e C o l o r } ) ;

r e t u r n { } ;

/ / W h a t w e r e t u r n c a n b e u s e d b y o t h e r m o d u l e s

} ) ;

There is however something missing from this example and it's the concept of registration.

Registering jQuery As An Async-compatible Module

One of the key features that landed in jQuery 1.7 was support for registering jQuery as an asynchronous

module. There are a number of compatible script loaders (including RequireJS and curl) which are

capable of loading modules using an asynchronous module format and this means fewer hacks are

required to get things working.

As a result of jQuery's popularity, AMD loaders need to take into account multiple versions of the library

being loaded into the same page as you ideally don't want several different versions loading at the same

time. Loaders have the option of either specifically taking this issue into account or instructing their

users that there are known issues with third party scripts and their libraries.

What the 1.7 addition brings to the table is that it helps avoid issues with other third party code on a

page accidentally loading up a version of jQuery on the page that the owner wasn't expecting. You don't

want other instances clobbering your own and so this can be of benefit.

The way this works is that the script loader being employed indicates that it supports multiple jQuery

versions by specifying that a property, d e f i n e . a m d . j Q u e r y is equal to true. For those interested in

more specific implementation details, we register jQuery as a named module as there is a risk that it can

be concatenated with other files which may use AMD's d e f i n e ( ) method, but not use a proper

concatenation script that understands anonymous AMD module definitions.

The named AMD provides a safety blanket of being both robust and safe for most use-cases.

/ / A c c o u n t f o r t h e e x i s t e n c e o f m o r e t h a n o n e g l o b a l

/ / i n s t a n c e s o f j Q u e r y i n t h e d o c u m e n t , c a t e r f o r t e s t i n g

Page 11: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

11/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

/ / . n o C o n f l i c t ( )

v a r j Q u e r y = t h i s . j Q u e r y | | " j Q u e r y " ,

$ = t h i s . $ | | " $ " ,

o r i g i n a l j Q u e r y = j Q u e r y ,

o r i g i n a l $ = $ ,

a m d D e f i n e d ;

d e f i n e ( [ ' j q u e r y ' ] , f u n c t i o n ( $ ) {

$ ( ' . i t e m s ' ) . c s s ( ' b a c k g r o u n d ' , ' g r e e n ' ) ;

r e t u r n f u n c t i o n ( ) { } ;

} ) ;

/ / T h e v e r y e a s y t o i m p l e m e n t f l a g s t a t i n g s u p p o r t w h i c h

/ / w o u l d b e u s e d b y t h e A M D l o a d e r

d e f i n e . a m d = {

j Q u e r y : t r u e

} ;

Smarter jQuery Plugins

I've recently discussed some ideas and examples of how jQuery plugins could be written using Universal

Module Definition (UMD) patterns here. UMDs define modules that can work on both the client and

server, as well as with all popular script loaders available at the moment. Whilst this is still a new area

with a lot of concepts still being finalized, feel free to look at the code samples in the section title AMD

&& CommonJS below and let me know if you feel there's anything we could do better.

What Script Loaders & Frameworks Support AMD?

In-browser:Server-side:

RequireJS http://requirejs.org

PINF http://github.com/pinf/loader-js

AMD Conclusions

The above are very trivial examples of just how useful AMD modules can truly be, but they hopefully

provide a foundation for understanding how they work.

You may be interested to know that many visible large applications and companies currently use AMD

Page 12: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

12/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

modules as a part of their architecture. These include IBM and the BBC iPlayer, which highlight just

how seriously this format is being considered by developers at an enterprise-level.

For more reasons why many developers are opting to use AMD modules in their applications, you may

be interested in this post by James Burke.

CommonJS A Module Format Optimized For The Server

CommonJS are a volunteer working group which aim to design, prototype and standardize JavaScript

APIs. To date they've attempted to ratify standards for both modules and packages. The CommonJS

module proposal specifies a simple API for declaring modules server-side and unlike AMD attempts to

cover a broader set of concerns such as io, filesystem, promises and more.

Getting Started

From a structure perspective, a CJS module is a reusable piece of JavaScript which exports specific

objects made available to any dependent code - there are typically no function wrappers around such

modules (so you won't see d e f i n e used here for example).

At a high-level they basically contain two primary parts: a free variable named e x p o r t s which

contains the objects a module wishes to make available to other modules and a r e q u i r e function that

modules can use to import the exports of other modules.

Understanding CJS: require() and exports

/ / p a c k a g e / l i b i s a d e p e n d e n c y w e r e q u i r e

v a r l i b = r e q u i r e ( ' p a c k a g e / l i b ' ) ;

/ / s o m e b e h a v i o u r f o r o u r m o d u l e

f u n c t i o n f o o ( ) {

l i b . l o g ( ' h e l l o w o r l d ! ' ) ;

}

/ / e x p o r t ( e x p o s e ) f o o t o o t h e r m o d u l e s

e x p o r t s . f o o = f o o ;

Basic consumption of exports

/ / d e f i n e m o r e b e h a v i o u r w e w o u l d l i k e t o e x p o s e

f u n c t i o n f o o b a r ( ) {

t h i s . f o o = f u n c t i o n ( ) {

Page 13: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

13/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

c o n s o l e . l o g ( ' H e l l o f o o ' ) ;

}

t h i s . b a r = f u n c t i o n ( ) {

c o n s o l e . l o g ( ' H e l l o b a r ' ) ;

}

}

/ / e x p o s e f o o b a r t o o t h e r m o d u l e s

e x p o r t s . f o o b a r = f o o b a r ;

/ / a n a p p l i c a t i o n c o n s u m i n g ' f o o b a r '

/ / a c c e s s t h e m o d u l e r e l a t i v e t o t h e p a t h

/ / w h e r e b o t h u s a g e a n d m o d u l e f i l e s e x i s t

/ / i n t h e s a m e d i r e c t o r y

v a r f o o b a r = r e q u i r e ( ' . / f o o b a r ' ) . f o o b a r ,

t e s t = n e w f o o b a r ( ) ;

t e s t . b a r ( ) ; / / ' H e l l o b a r '

AMD-equivalent Of The First CJS Example

d e f i n e ( [ ' p a c k a g e / l i b ' ] , f u n c t i o n ( l i b ) {

/ / s o m e b e h a v i o u r f o r o u r m o d u l e

f u n c t i o n f o o ( ) {

l i b . l o g ( ' h e l l o w o r l d ! ' ) ;

}

/ / e x p o r t ( e x p o s e ) f o o f o r o t h e r m o d u l e s

r e t u r n {

f o o b a r : f o o

} ;

} ) ;

Consuming Multiple Dependencies

app.js

Page 14: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

14/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

v a r m o d A = r e q u i r e ( ' . / f o o ' ) ;

v a r m o d B = r e q u i r e ( ' . / b a r ' ) ;

e x p o r t s . a p p = f u n c t i o n ( ) {

c o n s o l e . l o g ( ' I m a n a p p l i c a t i o n ! ' ) ;

}

e x p o r t s . f o o = f u n c t i o n ( ) {

r e t u r n m o d A . h e l l o W o r l d ( ) ;

}

bar.jse x p o r t s . n a m e = ' b a r ' ;

foo.jsr e q u i r e ( ' . / b a r ' ) ;

e x p o r t s . h e l l o W o r l d = f u n c t i o n ( ) {

r e t u r n ' H e l l o W o r l d ! ! ' '

}

What Loaders & Frameworks Support CJS?

In-browser:Server-side:

Is CJS Suitable For The Browser?

There are developers that feel CommonJS is better suited to server-side development which is one reason

there's currently a level of disagreement over which format should and will be used as the de facto

standard in the pre-Harmony age moving forward. Some of the arguments against CJS include a note

that many CommonJS APIs address server-oriented features which one would simply not be able to

implement at a browser-level in JavaScript - for example, io, system and js could be considered

unimplementable by the nature of their functionality.

That said, it's useful to know how to structure CJS modules regardless so that we can better appreciate

how they fit in when defining modules which may be used everywhere. Modules which have

applications on both the client and server include validation, conversion and templating engines. The

way some developers are approaching choosing which format to use is opting for CJS when a module

can be used in a server-side environment and using AMD if this is not the case.

As AMD modules are capable of using plugins and can define more granular things like constructors and

functions this makes sense. CJS modules are only able to define objects which can be tedious to work

with if you're trying to obtain constructors out of them.

Page 15: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

15/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

Although it's beyond the scope of this article, you may have also noticed that there were different types

of 'require' methods mentioned when discussing AMD and CJS.

The concern with a similar naming convention is of course confusion and the community are currently

split on the merits of a global require function. John Hann's suggestion here is that rather than calling it

'require', which would probably fail to achieve the goal of informing users about the different between a

global and inner require, it may make more sense to rename the global loader method something else

(e.g. the name of the library). It's for this reason that a loader like curl.js uses c u r l ( ) as opposed to

r e q u i r e .

AMD && CommonJS Competing, But Equally Valid Standards

Whilst this article has placed more emphasis on using AMD over CJS, the reality is that both formats are

valid and have a use.

AMD adopts a browser-first approach to development, opting for asynchronous behaviour and simplified

backwards compatability but it doesn't have any concept of File I/O. It supports objects, functions,

constructors, strings, JSON and many other types of modules, running natively in the browser. It's

incredibly flexible.

CommonJS on the other hand takes a server-first approach, assuming synchronous behaviour, no

global baggage as John Hann would refer to it as and it attempts to cater for the future (on the server).

What we mean by this is that because CJS supports unwrapped modules, it can feel a little more close to

the ES.next/Harmony specifications, freeing you of the d e f i n e ( ) wrapper that AMD enforces. CJS

modules however only support objects as modules.

Although the idea of yet another module format may be daunting, you may be interested in some

samples of work on hybrid AMD/CJS and Univeral AMD/CJS modules.

Basic AMD Hybrid Format (John Hann)

d e f i n e ( f u n c t i o n ( r e q u i r e , e x p o r t s , m o d u l e ) {

v a r s h u f f l e r = r e q u i r e ( ' l i b / s h u f f l e ' ) ;

e x p o r t s . r a n d o m i z e = f u n c t i o n ( i n p u t ) {

r e t u r n s h u f f l e r . s h u f f l e ( i n p u t ) ;

}

} ) ;

AMD/CommonJS Universal Module Definition (Variation 2, UMDjs)

Page 16: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

16/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

/ * *

* e x p o r t s o b j e c t b a s e d v e r s i o n , i f y o u n e e d t o m a k e a

* c i r c u l a r d e p e n d e n c y o r n e e d c o m p a t i b i l i t y w i t h

* c o m m o n j s - l i k e e n v i r o n m e n t s t h a t a r e n o t N o d e .

* /

( f u n c t i o n ( d e f i n e ) {

/ / T h e ' i d ' i s o p t i o n a l , b u t r e c o m m e n d e d i f t h i s i s

/ / a p o p u l a r w e b l i b r a r y t h a t i s u s e d m o s t l y i n

/ / n o n - A M D / N o d e e n v i r o n m e n t s . H o w e v e r , i f w a n t

/ / t o m a k e a n a n o n y m o u s m o d u l e , r e m o v e t h e ' i d '

/ / b e l o w , a n d r e m o v e t h e i d u s e i n t h e d e f i n e s h i m .

d e f i n e ( ' i d ' , f u n c t i o n ( r e q u i r e , e x p o r t s ) {

/ / I f h a v e d e p e n d e n c i e s , g e t t h e m h e r e

v a r a = r e q u i r e ( ' a ' ) ;

/ / A t t a c h p r o p e r t i e s t o e x p o r t s .

e x p o r t s . n a m e = v a l u e ;

} ) ;

} ( t y p e o f d e f i n e = = = ' f u n c t i o n ' & & d e f i n e . a m d ? d e f i n e : f u n c t i o n ( i d , f a c t o r y ) {

i f ( t y p e o f e x p o r t s ! = = ' u n d e f i n e d ' ) {

/ / c o m m o n j s

f a c t o r y ( r e q u i r e , e x p o r t s ) ;

} e l s e {

/ / C r e a t e a g l o b a l f u n c t i o n . O n l y w o r k s i f

/ / t h e c o d e d o e s n o t h a v e d e p e n d e n c i e s , o r

/ / d e p e n d e n c i e s f i t t h e c a l l p a t t e r n b e l o w .

f a c t o r y ( f u n c t i o n ( v a l u e ) {

r e t u r n w i n d o w [ v a l u e ] ;

} , ( w i n d o w [ i d ] = { } ) ) ;

}

} ) ) ;

Extensible UMD Plugins With (Variation by myself and Thomas Davis).

core.js/ / M o d u l e / P l u g i n c o r e

/ / N o t e : t h e w r a p p e r c o d e y o u s e e a r o u n d t h e m o d u l e i s w h a t e n a b l e s

/ / u s t o s u p p o r t m u l t i p l e m o d u l e f o r m a t s a n d s p e c i f i c a t i o n s b y

/ / m a p p i n g t h e a r g u m e n t s d e f i n e d t o w h a t a s p e c i f i c f o r m a t e x p e c t s

/ / t o b e p r e s e n t . O u r a c t u a l m o d u l e f u n c t i o n a l i t y i s d e f i n e d l o w e r

/ / d o w n , w h e r e a n a m e d m o d u l e a n d e x p o r t s a r e d e m o n s t r a t e d .

Page 17: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

17/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

; ( f u n c t i o n ( n a m e , d e f i n i t i o n ) {

v a r t h e M o d u l e = d e f i n i t i o n ( ) ,

/ / t h i s i s c o n s i d e r e d " s a f e " :

h a s D e f i n e = t y p e o f d e f i n e = = = ' f u n c t i o n ' & & d e f i n e . a m d ,

/ / h a s D e f i n e = t y p e o f d e f i n e = = = ' f u n c t i o n ' ,

h a s E x p o r t s = t y p e o f m o d u l e ! = = ' u n d e f i n e d ' & & m o d u l e . e x p o r t s ;

i f ( h a s D e f i n e ) { / / A M D M o d u l e

d e f i n e ( t h e M o d u l e ) ;

} e l s e i f ( h a s E x p o r t s ) { / / N o d e . j s M o d u l e

m o d u l e . e x p o r t s = t h e M o d u l e ;

} e l s e { / / A s s i g n t o c o m m o n n a m e s p a c e s o r s i m p l y t h e g l o b a l o b j e c t ( w i n d o w )

( t h i s . j Q u e r y | | t h i s . e n d e r | | t h i s . $ | | t h i s ) [ n a m e ] = t h e M o d u l e ;

}

} ) ( ' c o r e ' , f u n c t i o n ( ) {

v a r m o d u l e = t h i s ;

m o d u l e . p l u g i n s = [ ] ;

m o d u l e . h i g h l i g h t C o l o r = " y e l l o w " ;

m o d u l e . e r r o r C o l o r = " r e d " ;

/ / d e f i n e t h e c o r e m o d u l e h e r e a n d r e t u r n t h e p u b l i c A P I

/ / t h i s i s t h e h i g h l i g h t m e t h o d u s e d b y t h e c o r e h i g h l i g h t A l l ( )

/ / m e t h o d a n d a l l o f t h e p l u g i n s h i g h l i g h t i n g e l e m e n t s d i f f e r e n t

/ / c o l o r s

m o d u l e . h i g h l i g h t = f u n c t i o n ( e l , s t r C o l o r ) {

/ / t h i s m o d u l e u s e s j Q u e r y , h o w e v e r p l a i n o l d J a v a S c r i p t

/ / o r s a y , D o j o c o u l d b e j u s t a s e a s i l y u s e d .

i f ( t h i s . j Q u e r y ) {

j Q u e r y ( e l ) . c s s ( ' b a c k g r o u n d ' , s t r C o l o r ) ;

}

}

r e t u r n {

h i g h l i g h t A l l : f u n c t i o n ( ) {

m o d u l e . h i g h l i g h t ( ' d i v ' , m o d u l e . h i g h l i g h t C o l o r ) ;

}

} ;

} ) ;

myExtension.js; ( f u n c t i o n ( n a m e , d e f i n i t i o n ) {

Page 18: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

18/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

v a r t h e M o d u l e = d e f i n i t i o n ( ) ,

h a s D e f i n e = t y p e o f d e f i n e = = = ' f u n c t i o n ' ,

h a s E x p o r t s = t y p e o f m o d u l e ! = = ' u n d e f i n e d ' & & m o d u l e . e x p o r t s ;

i f ( h a s D e f i n e ) { / / A M D M o d u l e

d e f i n e ( t h e M o d u l e ) ;

} e l s e i f ( h a s E x p o r t s ) { / / N o d e . j s M o d u l e

m o d u l e . e x p o r t s = t h e M o d u l e ;

} e l s e { / / A s s i g n t o c o m m o n n a m e s p a c e s o r s i m p l y t h e g l o b a l o b j e c t ( w i n d o w )

/ / a c c o u n t f o r f o r f l a t - f i l e / g l o b a l m o d u l e e x t e n s i o n s

v a r o b j = n u l l ;

v a r n a m e s p a c e s = n a m e . s p l i t ( " . " ) ;

v a r s c o p e = ( t h i s . j Q u e r y | | t h i s . e n d e r | | t h i s . $ | | t h i s ) ;

f o r ( v a r i = 0 ; i

a p p . j s

$ ( f u n c t i o n ( ) {

/ / t h e p l u g i n ' c o r e ' i s e x p o s e d u n d e r a c o r e n a m e s p a c e i n

/ / t h i s e x a m p l e w h i c h w e f i r s t c a c h e

v a r c o r e = $ . c o r e ;

/ / u s e t h e n u s e s o m e o f t h e b u i l t - i n c o r e f u n c t i o n a l i t y t o

/ / h i g h l i g h t a l l d i v s i n t h e p a g e y e l l o w

c o r e . h i g h l i g h t A l l ( ) ;

/ / a c c e s s t h e p l u g i n s ( e x t e n s i o n s ) l o a d e d i n t o t h e ' p l u g i n '

/ / n a m e s p a c e o f o u r c o r e m o d u l e :

/ / S e t t h e f i r s t d i v i n t h e p a g e t o h a v e a g r e e n b a c k g r o u n d .

c o r e . p l u g i n . s e t G r e e n ( " d i v : f i r s t " ) ;

/ / H e r e w e ' r e m a k i n g u s e o f t h e c o r e ' s ' h i g h l i g h t ' m e t h o d

/ / u n d e r t h e h o o d f r o m a p l u g i n l o a d e d i n a f t e r i t

/ / S e t t h e l a s t d i v t o t h e ' e r r o r C o l o r ' p r o p e r t y d e f i n e d i n

/ / o u r c o r e m o d u l e / p l u g i n . I f y o u r e v i e w t h e c o d e f u r t h e r d o w n

/ / y o u ' l l s e e h o w e a s y i t i s t o c o n s u m e p r o p e r t i e s a n d m e t h o d s

/ / b e t w e e n t h e c o r e a n d o t h e r p l u g i n s

c o r e . p l u g i n . s e t R e d ( ' d i v : l a s t ' ) ;

} ) ;

Page 19: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

19/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

E S H a r m o n y M o d u l e s O f T h e F u t u r e

T C 3 9 , t h e s t a n d a r d s b o d y c h a r g e d w i t h d e f i n i n g t h e s y n t a x a n d s e m a n t i c s o f E C M A S c r i p t

a n d i t s f u t u r e i t e r a t i o n s i s c o m p o s e d o f a n u m b e r o f v e r y i n t e l l i g e n t d e v e l o p e r s . S o m e

o f t h e s e d e v e l o p e r s ( s u c h a s A l e x R u s s e l l ) h a v e b e e n k e e p i n g a c l o s e e y e o n t h e

e v o l u t i o n o f J a v a S c r i p t u s a g e f o r l a r g e - s c a l e d e v e l o p m e n t o v e r t h e p a s t f e w y e a r s a n d

a r e a c u t e l y a w a r e o f t h e n e e d f o r b e t t e r l a n g u a g e f e a t u r e s f o r w r i t i n g m o r e m o d u l a r

J S .

F o r t h i s r e a s o n , t h e r e a r e c u r r e n t l y p r o p o s a l s f o r a n u m b e r o f e x c i t i n g a d d i t i o n s t o

t h e l a n g u a g e i n c l u d i n g f l e x i b l e m o d u l e s t h a t c a n w o r k o n b o t h t h e c l i e n t a n d s e r v e r , a

m o d u l e l o a d e r a n d m o r e . I n t h i s s e c t i o n , I ' l l b e s h o w i n g y o u s o m e c o d e s a m p l e s o f t h e

s y n t a x f o r m o d u l e s i n E S . n e x t s o y o u c a n g e t a t a s t e o f w h a t ' s t o c o m e .

N o t e : A l t h o u g h H a r m o n y i s s t i l l i n t h e p r o p o s a l p h a s e s , y o u c a n a l r e a d y t r y o u t

( p a r t i a l ) f e a t u r e s o f E S . n e x t t h a t a d d r e s s n a t i v e s u p p o r t f o r w r i t i n g m o d u l a r

J a v a S c r i p t t h a n k s t o G o o g l e ' s T r a c e u r c o m p i l e r . T o g e t u p a n d r u n n i n g w i t h T r a c e u r i n

u n d e r a m i n u t e , r e a d t h i s g e t t i n g s t a r t e d g u i d e . T h e r e ' s a l s o a J S C o n f p r e s e n t a t i o n

a b o u t i t t h a t ' s w o r t h l o o k i n g a t i f y o u ' r e i n t e r e s t e d i n l e a r n i n g m o r e a b o u t t h e

p r o j e c t .

M o d u l e s W i t h I m p o r t s A n d E x p o r t s

I f y o u ' v e r e a d t h r o u g h t h e s e c t i o n s o n A M D a n d C J S m o d u l e s y o u m a y b e f a m i l i a r w i t h

t h e c o n c e p t o f m o d u l e d e p e n d e n c i e s ( i m p o r t s ) a n d m o d u l e e x p o r t s ( o r , t h e p u b l i c

A P I / v a r i a b l e s w e a l l o w o t h e r m o d u l e s t o c o n s u m e ) . I n E S . n e x t , t h e s e c o n c e p t s h a v e b e e n

p r o p o s e d i n a s l i g h t l y m o r e s u c c i n c t m a n n e r w i t h d e p e n d e n c i e s b e i n g s p e c i f i e d u s i n g a n

Page 20: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

20/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

i m p o r t k e y w o r d . e x p o r t i s n ' t g r e a t l y d i f f e r e n t t o w h a t w e m i g h t e x p e c t a n d I t h i n k

m a n y d e v e l o p e r s w i l l l o o k a t t h e c o d e b e l o w a n d i n s t a n t l y ' g e t ' i t .

import declarations bind a module's exports as local variables and may be renamed to avoid name

collisions/conflicts.

export declarations declare that a local-binding of a module is externally visible such that other

modules may read the exports but can't modify them. Interestingly, modules may export child

modules however can't export modules that have been defined elsewhere. You may also rename

exports so their external name differs from their local names.

m o d u l e s t a f f {

/ / s p e c i f y ( p u b l i c ) e x p o r t s t h a t c a n b e c o n s u m e d b y

/ / o t h e r m o d u l e s

e x p o r t v a r b a k e r = {

b a k e : f u n c t i o n ( i t e m ) {

c o n s o l e . l o g ( ' W o o ! I j u s t b a k e d ' + i t e m ) ;

}

}

}

m o d u l e s k i l l s {

e x p o r t v a r s p e c i a l t y = " b a k i n g " ;

e x p o r t v a r e x p e r i e n c e = " 5 y e a r s " ;

}

m o d u l e c a k e F a c t o r y {

/ / s p e c i f y d e p e n d e n c i e s

i m p o r t b a k e r f r o m s t a f f ;

/ / i m p o r t e v e r y t h i n g w i t h w i l d c a r d s

i m p o r t * f r o m s k i l l s ;

e x p o r t v a r o v e n = {

m a k e C u p c a k e : f u n c t i o n ( t o p p i n g s ) {

b a k e r . b a k e ( ' c u p c a k e ' , t o p p i n g s ) ;

} ,

m a k e M u f f i n : f u n c t i o n ( m S i z e ) {

Page 21: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

21/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

b a k e r . b a k e ( ' m u f f i n ' , s i z e ) ;

}

}

}

Modules Loaded From Remote Sources

The module proposals also cater for modules which are remotely based (e.g. a third-party API wrapper)

making it simplistic to load modules in from external locations. Here's an example of us pulling in the

module we defined above and utilizing it:

m o d u l e c a k e F a c t o r y f r o m ' h t t p : / / a d d y o s m a n i . c o m / f a c t o r y / c a k e s . j s ' ;

c a k e F a c t o r y . o v e n . m a k e C u p c a k e ( ' s p r i n k l e s ' ) ;

c a k e F a c t o r y . o v e n . m a k e M u f f i n ( ' l a r g e ' ) ;

Module Loader API

The module loader proposed describes a dynamic API for loading modules in highly controlled contexts.

Signatures supported on the loader include l o a d ( u r l , m o d u l e I n s t a n c e , e r r o r ) for loading

modules, c r e a t e M o d u l e ( o b j e c t , g l o b a l M o d u l e R e f e r e n c e s ) and others. Here's another

example of us dynamically loading in the module we initially defined. Note that unlike the last example

where we pulled in a module from a remote source, the module loader API is better suited to dynamic

contexts.

L o a d e r . l o a d ( ' h t t p : / / a d d y o s m a n i . c o m / f a c t o r y / c a k e s . j s ' ,

f u n c t i o n ( c a k e F a c t o r y ) {

c a k e F a c t o r y . o v e n . m a k e C u p c a k e ( ' c h o c o l a t e ' ) ;

} ) ;

CommonJS-like Modules For The Server

For developers who are server-oriented, the module system proposed for ES.next isn't just constrained to

looking at modules in the browser. Below for examples, you can see a CJS-like module proposed for use

on the server:

/ / i o / F i l e . j s

e x p o r t f u n c t i o n o p e n ( p a t h ) { . . . } ;

e x p o r t f u n c t i o n c l o s e ( h n d ) { . . . } ;

/ / c o m p i l e r / L e x i c a l H a n d l e r . j s

m o d u l e f i l e f r o m ' i o / F i l e ' ;

Page 22: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

22/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

i m p o r t { o p e n , c l o s e } f r o m f i l e ;

e x p o r t f u n c t i o n s c a n ( i n ) {

t r y {

v a r h = o p e n ( i n ) . . .

}

f i n a l l y { c l o s e ( h ) }

}

m o d u l e l e x e r f r o m ' c o m p i l e r / L e x i c a l H a n d l e r ' ;

m o d u l e s t d l i b f r o m ' @ s t d ' ;

/ / . . . s c a n ( c m d l i n e [ 0 ] ) . . .

Classes With Constructors, Getters & Setters

The notion of a class has always been a contentious issue with purists and we've so far got along with

either falling back on JavaScript's prototypal nature or through using frameworks or abstractions that

offer the ability to use class definitions in a form that desugars to the same prototypal behavior.

In Harmony, classes come as part of the language along with constructors and (finally) some sense of

true privacy. In the following examples, I've included some inline comments to help you understand

how classes are structured, but you may also notice the lack of the word 'function' in here. This isn't a

typo error: TC39 have been making a conscious effort to decrease our abuse of the f u n c t i o n keyword

for everything and the hope is that this will help simplify how we write code.

c l a s s C a k e {

/ / W e c a n d e f i n e t h e b o d y o f a c l a s s ' c o n s t r u c t o r

/ / f u n c t i o n b y u s i n g t h e k e y w o r d ' c o n s t r u c t o r ' f o l l o w e d

/ / b y a n a r g u m e n t l i s t o f p u b l i c a n d p r i v a t e d e c l a r a t i o n s .

c o n s t r u c t o r ( n a m e , t o p p i n g s , p r i c e , c a k e S i z e ) {

p u b l i c n a m e = n a m e ;

p u b l i c c a k e S i z e = c a k e S i z e ;

p u b l i c t o p p i n g s = t o p p i n g s ;

p r i v a t e p r i c e = p r i c e ;

}

/ / A s a p a r t o f E S . n e x t ' s e f f o r t s t o d e c r e a s e t h e u n n e c e s s a r y

/ / u s e o f ' f u n c t i o n ' f o r e v e r y t h i n g , y o u ' l l n o t i c e t h a t i t ' s

/ / d r o p p e d f o r c a s e s s u c h a s t h e f o l l o w i n g . H e r e a n i d e n t i f i e r

Page 23: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

23/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

/ / f o l l o w e d b y a n a r g u m e n t l i s t a n d a b o d y d e f i n e s a n e w m e t h o d

a d d T o p p i n g ( t o p p i n g ) {

p u b l i c ( t h i s ) . t o p p i n g s . p u s h ( t o p p i n g ) ;

}

/ / G e t t e r s c a n b e d e f i n e d b y d e c l a r i n g g e t b e f o r e

/ / a n i d e n t i f i e r / m e t h o d n a m e a n d a c u r l y b o d y .

g e t a l l T o p p i n g s ( ) {

r e t u r n p u b l i c ( t h i s ) . t o p p i n g s ;

}

g e t q u a l i f i e s F o r D i s c o u n t ( ) {

r e t u r n p r i v a t e ( t h i s ) . p r i c e > 5 ;

}

/ / S i m i l a r t o g e t t e r s , s e t t e r s c a n b e d e f i n e d b y u s i n g

/ / t h e ' s e t ' k e y w o r d b e f o r e a n i d e n t i f i e r

s e t c a k e S i z e ( c S i z e ) {

i f ( c S i z e

E S H a r m o n y C o n c l u s i o n s

A s y o u c a n s e e , E S . n e x t i s c o m i n g w i t h s o m e e x c i t i n g n e w a d d i t i o n s . A l t h o u g h T r a c e u r

c a n b e u s e d t o a n e x t e n t t o t r y o u r s u c h f e a t u r e s i n t h e p r e s e n t , r e m e m b e r t h a t i t m a y

n o t b e t h e b e s t i d e a t o p l a n o u t y o u r s y s t e m t o u s e H a r m o n y ( j u s t y e t ) . T h e r e a r e

r i s k s h e r e s u c h a s s p e c i f i c a t i o n s c h a n g i n g a n d a p o t e n t i a l f a i l u r e a t t h e c r o s s -

b r o w s e r l e v e l ( I E 9 f o r e x a m p l e w i l l t a k e a w h i l e t o d i e ) s o y o u r b e s t b e t s u n t i l w e

h a v e b o t h s p e c f i n a l i z a t i o n a n d c o v e r a g e a r e A M D ( f o r i n - b r o w s e r m o d u l e s ) a n d C J S ( f o r

t h o s e o n t h e s e r v e r ) .

C o n c l u s i o n s A n d F u r t h e r R e a d i n g :

Page 24: Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyosmani

11/24/11 Writing Modular JavaScript With AMD, CommonJS & ES Harmony — addyo…

24/24www.readability.com/articles/ummplmts?legacy_bookmarklet=1#

A n d t h a t ' s i t f o r n o w . I f y o u h a v e f u r t h e r q u e s t i o n s a b o u t a n y o f t h e t o p i c s c o v e r e d

t o d a y , f e e l f r e e t o h i t m e u p o n t w i t t e r a n d I ' l l d o m y b e s t t o h e l p !

T h e t e c h n i c a l r e v i e w f o r t h i s a r t i c l e w a s d o i n g u s i n g D i i g o ( f o r G o o g l e C h r o m e ) . D i i g o

i s a f r e e t o o l t h a t a l l o w s y o u t o a d d b o t h c o m m e n t s a n d h i g h l i g h t s t o a n y l i v e

d o c u m e n t o n t h e w e b a n d i f t h e r e a r e c o r r e c t i o n s o r e x t e n s i o n s y o u w o u l d l i k e t o

s u g g e s t , p l e a s e u s e e i t h e r D i i g o ( o r a G i t H u b g i s t ) a n d I ' l l d o m y t e s t t o a d d r e s s a n y

p o i n t s y o u s e n d o v e r .

C o p y r i g h t A d d y O s m a n i , 2 0 1 1 . A l l R i g h t s R e s e r v e d .

Original URL:

http://addyosmani.com/writing-modular-js/

Share this article