Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Deep Dive Into JavaScript Modules

toshsharma
November 16, 2013

A Deep Dive Into JavaScript Modules

Today, large applications are being built with JavaScript, running in the browser as well as on servers. Although the language doesn't currently provide built-in support for modules, developers use multiple mechanisms to modularize their JavaScript code. In this talk, I will discuss several ways to create modules in JavaScript without polluting the global scope. In the process, I'll also clarify the relation between CommonJS modules, AMD, Node.js and RequireJS. In the end, I will also talk about the upcoming module functionality that will be a part of the next version of JavaScript (ES 6/ES.next).

toshsharma

November 16, 2013
Tweet

More Decks by toshsharma

Other Decks in Technology

Transcript

  1. A Deep Dive Into JavaScript Modules by / Ashutosh Sharma

    @zorder Senior Computer Scientist (Web Platform & Authoring)
  2. Quick Poll Spaghetti code? Need to maintain correct order of

    <script> tags? Polluted global namespace with clashing symbols? Difficulty in lazy loading scripts?
  3. The Problem Increasing code complexity Developers want discrete JS files

    Need for optimized deployment Good debugging support
  4. Modular JavaScript encapsulate a piece of code into a unit

    register its capability refer to other units as dependencies
  5. The Old Way No support for modules in the language

    Scripts are loaded via <script> tags Dependencies are stated via the order of <script> tags Global functions as event handlers < s c r i p t s r c = " j q u e r y . j s " > < / s c r i p t > < s c r i p t s r c = " j q u e r y . c o l o r - p l u g i n . j s " > < / s c r i p t >
  6. Avoiding Name Clashes Specially named (prefixed) global functions Employing the

    object literal or the classic module pattern < b u t t o n o n c l i c k = " s u r v e y _ f o r m _ _ o n s u b m i t ( ) " > S u b m i t < / b u t t o n >
  7. Object Literal Pattern v a r m y M o

    d u l e = { f o o : f u n c t i o n ( ) { c o n s o l e . l o g ( " f o o ! " ) ; } , b a r : f u n c t i o n ( ) { c o n s o l e . l o g ( " b a r ! " ) ; } } ; / / . . . m y M o d u l e . f o o ( ) ;
  8. Object Literal Pattern: Data v a r m y M

    o d u l e = { o p t i o n s : { n a m e : " J o h n D o e " , a g e : 3 0 } , i n i t : f u n c t i o n ( n a m e , a g e ) { t h i s . o p t i o n s . n a m e = n a m e ; t h i s . o p t i o n s . a g e = a g e ; } , f o o : f u n c t i o n ( ) { c o n s o l e . l o g ( " f o o ! " ) ; } } ;
  9. Object Literal Pattern: Nesting v a r U t i

    l = { L o g g i n g : { i n f o : f u n c t i o n ( ) { . . . } , d e b u g : f u n c t i o n ( ) { . . . } } , D a t a : { f e t c h : f u n c t i o n ( ) { . . . } , s a v e : f u n c t i o n ( ) { . . . } } } ; U t i l . D a t a . f e t c h ( ) ;
  10. Module Pattern Anonymous Closures ( f u n c t

    i o n ( ) { / / p r i v a t e s c o p e ! / / p r i v a t e s t a t e ! } ) ( ) ; IIFE (Immediately Invoked Function Expression) The function creates a new scope with access to global variables The closure provides us private state for the application's lifetime
  11. Module Pattern Importing Globals ( f u n c t

    i o n ( $ , d o c u m e n t , u n d e f i n e d ) { / / $ r e f e r s t o t h e j Q u e r y g l o b a l h e r e } ) ( j Q u e r y , d o c u m e n t ) ; Cleaner and faster
  12. Module Pattern Exporting a Module v a r m y

    M o d u l e = ( f u n c t i o n ( ) { v a r p r i v a t e D a t a = 4 2 ; v a r p r i v a t e M e t h o d = f u n c t i o n ( ) { . . . } ; r e t u r n { n a m e : " J o h n D o e " , / / < = p u b l i c f o o : f u n c t i o n ( ) { . . . } , / / < = p u b l i c b a r : f u n c t i o n ( ) { . . . } / / < = p u b l i c } ; } ) ( ) ; Return a value from the anonymous function This is the module's public interface We can also maintain private state and have private methods
  13. Object Literal And Module Patterns Using them together v a

    r U t i l = { D a t a : ( f u n c t i o n ( ) { v a r p r i v a t e D a t a = 4 2 ; r e t u r n { f e t c h : f u n c t i o n ( ) { . . . } , s a v e : f u n c t i o n ( ) { . . . } } ; } ) ( ) } ; U t i l . D a t a . f e t c h ( ) ;
  14. Object Literal And Module Patterns Review Encapsulate pieces of code

    into units Pollute the global namespace Provide no dependency management
  15. CommonJS A volunteer group for standardizing JavaScript APIs Write once,

    run everywhere e.g. web servers, command-line applications, desktop applications
  16. CommonJS Modules Module Structure v a r l o g

    = r e q u i r e ( " . / l o g g e r " ) ; f u n c t i o n f o o ( ) { l o g . w r i t e ( " f o o ! " ) ; } e x p o r t s . f o o = f o o ; logger.js: e x p o r t s . w r i t e = f u n c t i o n ( m s g ) { c o n s o l e . l o g ( m s g ) ; }
  17. CommonJS Modules Exporting Constructors person.js: f u n c t

    i o n P e r s o n ( n a m e , a g e ) { t h i s . n a m e = n a m e ; t h i s . a g e = a g e ; t h i s . s a y N a m e = f u n c t i o n ( ) { c o n s o l e . l o g ( n a m e ) ; } t h i s . s a y A g e = f u n c t i o n ( ) { c o n s o l e . l o g ( a g e ) ; } } e x p o r t s . P e r s o n = P e r s o n ; main.js: v a r P e r s o n = r e q u i r e ( " . / p e r s o n " ) . P e r s o n ; v a r j o h n = n e w P e r s o n ( " J o h n " , 3 0 ) ; j o h n . s a y N a m e ( ) ; / / = > J o h n
  18. CommonJS Modules module e x p o r t s

    . w r i t e = f u n c t i o n ( m s g ) { c o n s o l e . l o g ( m s g ) ; } c o n s o l e . l o g ( m o d u l e ) ; { i d : ' / U s e r s / a s h u t o s h / a p p / l o g g e r . j s ' , e x p o r t s : { w r i t e : [ F u n c t i o n ] } , p a r e n t : { . . . } , l o a d e d : f a l s e , c h i l d r e n : [ ] , f i l e n a m e : ' / U s e r s / a s h u t o s h / a p p / l o g g e r . j s ' , p a t h s : [ ' / U s e r s / a s h u t o s h / a p p / n o d e _ m o d u l e s ' , ' / U s e r s / a s h u t o s h / n o d e _ m o d u l e s ' , ' / U s e r s / n o d e _ m o d u l e s ' , ' / n o d e _ m o d u l e s ' ] }
  19. Issues With CommonJS Modules require() is a synchronous call Only

    one module per file Additional steps required for modules to work in browsers
  20. Asynchronous Module Definition define() 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 * / m o d u l e _ d e f i n i t i o n _ f u n c t i o n ) ;
  21. Asynchronous Module Definition define() d e f i n e

    ( [ ' f o o ' , ' b a r ' ] , f u n c t i o n ( f o o , b a r ) { v a r m y M o d u l e = { f o o : f u n c t i o n ( ) { / * . . . * / } } ; r e t u r n m y M o d u l e ; } ) ;
  22. Asynchronous Module Definition require() r e q u i r

    e ( [ d e p e n d e n c i e s ] , c a l l b a c k _ f u n c 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 ) { f o o . p r o c e s s ( b a r ( ) ) ; } ) ;
  23. Asynchronous Module Definition Dynamically Loading 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 r e a d y = f a l s e , c o u n t = 0 ; 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 a d y = t r u e ; c o u n t = f o o . c o u n t ( ) + b a r . c o u n t ( ) ; } ) ; r e t u r n { r e a d y : r e a d y , g e t C o u n t : f u n c t i o n ( ) { r e t u r n c o u n t ; } } } ) ;
  24. Asynchronous Module Definition AMD Plugins d e f i n

    e ( [ ' t e x t ! . . / t e m p l a t e s / e n t r y . h t m l ' , ' f o o ' ] , f u n c t i o n ( t e m p l a t e , f o o ) { / / d o s o m e t h i n g w i t h t h e t e m p l a t e t e x t s t r i n g . } ) ;
  25. Asynchronous Module Definition Simplified CommonJS Wrapping 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 ) { / / T r a d i t i o n a l C o m m o n J S m o d u l e c o n t e n t h e r e } ) ; 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 f o o = r e q u i r e ( ' f o o ' ) , b a r = r e q u i r e ( ' b a r ' ) ; e x p o r t s . f o o b a r = f u n c t i o n ( ) { f o o . c o u n t ( ) + b a r . c o u n t ( ) } ; } ) ;
  26. Asynchronous Module Definition Advantages Avoids pollution of the global namespace

    Load modules in the browser without a build process Multiple modules can be bundled in a single file Lazy loading of scripts is possible
  27. RequireJS Basic Usage index.html: < s c r i p

    t d a t a - m a i n = " s c r i p t s / m a i n " s r c = " r e q u i r e . j s " > < / s c r i p t > scripts/main.js: r e q u i r e ( [ ' h e l p e r / u t i l ' ] , f u n c t i o n ( u t i l ) { / / . . . } ) ;
  28. RequireJS Optimizer Tool Bundle JavaScript files and modules into a

    single file Minify the result Convert CommonJS modules to the AMD format
  29. Sharing Code AMD-compatible Wrapper + RequireJS e.g. Used by Adobe

    Brackets Browserify Use globals in the browser amdefine 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 ) { / * . . . * / e x p o r t s . f o o = f u n c t i o n ( ) { / * . . . * / } } ) ; ( f u n c t i o n ( e x p o r t s ) { / * . . . * / e x p o r t s . f o o = f u n c t i o n ( ) { / * . . . * / } } ) ( t y p e o f e x p o r t s ! = = " u n d e f i n e d " ? e x p o r t s : t h i s [ ' m y M o d u l e ' ] = { } ) ;
  30. Sharing Code Universal Module Definition (UMD) ( f u n

    c t i o n ( d e f i n e ) { 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 ) { v a r a = r e q u i r e ( ' a ' ) ; 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 . 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 ] = { } ) ) ; } } ) ) ; Works in both CommonJS and AMD environments Optionally calls define, if available Optionally uses exports, if available
  31. ES6 Modules Exporting / / c i r c l

    e . j s l e t f u n c t i o n s q ( n ) { r e t u r n n * n ; } / / p r i v a t e e x p o r t c o n s t P I = 3 . 1 4 1 5 9 e x p o r t f u n c t i o n a r e a ( r ) { r e t u r n P I * r * r ; } / / s c r i p t s / c i r c l e . j s c o n s t P I = 3 . 1 4 1 5 9 f u n c t i o n a r e a ( r ) { r e t u r n P I * r * r ; } e x p o r t { P I , a r e a } ; e x p o r t { P I a s a p p r o x i m a t e P I , a r e a } ;
  32. ES6 Modules Importing / / m a i n .

    j s i m p o r t { a r e a } f r o m ' s c r i p t s / c i r c l e ' ; c o n s o l e . l o g ( a r e a ( 5 ) ) ; i m p o r t { a r e a , P I } f r o m ' s c r i p t s / c i r c l e ' ; i m p o r t ' s c r i p t s / c i r c l e ' a s c i r c l e ; c o n s o l e . l o g ( c i r c l e . a r e a ( 5 ) ) ; i m p o r t { a r e a a s a r e a O f C i r c l e } f r o m ' s c r i p t s / c i r c l e ' ; c o n s o l e . l o g ( a r e a O f C i r c l e ( 5 ) ) ;
  33. ES6 Modules Inline Modules m o d u l e

    ' s c r i p t s / c i r c l e ' { / / . . . } m o d u l e ' s c r i p t s / r e c t a n g l e ' { / / . . . } m o d u l e ' f o o ' { / / . . . }
  34. ES6 Modules Module Loader API S y s t e

    m . i m p o r t ( [ ' f o o ' , ' b a r ' ] , f u n c t i o n ( f o o , b a r ) { / / s u c c e s s / / . . . } , f u n c t i o n ( e r r o r ) { / / f a i l u r e / / . . . } ) ; S y s t e m . l o a d ( ' p a t h / t o / j q u e r y . j s ' , f u n c t i o n ( ) { / / . . . } ) ;
  35. Further Reading Using Objects to Organize Your Code JavaScript Module

    Pattern: In-Depth Why Web Modules? Writing Modular JavaScript With AMD, CommonJS & ES Harmony CommonJS Modules 1.1.1 CommonJS Notes Asynchronous Module Definition Why AMD?
  36. Further Reading Universal Module Definition (UMD) RequireJS API AMD Patterns

    Writing for Node and the Browser Almond AMD Loader amdefine Harmony Modules ECMAScript 6 Modules ES6 Resources For The Curious JavaScripter