javascript modules


About Me



Barak Igal
Front End Developer
Wix TPA Team


I love javascript


Why?


it's all about Freedom


we have a problem

script MANAGEMENT

...<script src="jquery.js" type="text/javascript"></script><script src="jquery.plugin.js" type="text/javascript"></script><script src="view.js" type="text/javascript"></script><script src="widget.js" type="text/javascript"></script><script src="i18n.js" type="text/javascript"></script><script src="app.js" type="text/javascript"></script><script src="strings.js" type="text/javascript"></script>...
Can you spot the bug?

PROS

  • No setup upfront. 
  • Server guys don't know javascipt.


cons

  • Insert script tags manually.               
  • Keep the scripts in order.
  • Global scope pollution.
  • Blocking/Synchronize.

    the script tag guides


    • Don't write your javascript in one big file. Separate it to smaller files.
    • When you have lots of small pieces you need to start working with dependencies and the worst of all - dependency management.
    • Manual dependency management using script tags, might fit for a small project, but.... there are no small projects.
    • Build system alone still doesn't  solve the problem, one still needs to keep the scripts in order.

    dependency mess



    NAMESPACES

    definition


    //define namesapcesvar App = {};
    App.Views = {};
    App.Views.Home = {};
    App.Views.Home.Config = {};
    
    //get some property of deep namespace
    var myColor = App.Views.Home.Config.backgroundColor;

    get unknown namespace


    if(App && App.HasThisProp && App.HasThisProp.AndThisProp && App.HasThisProp.AndThisProp.SomeWantedNamespace && App...........)

    Solution?
    var closer = App.HasThisProp.AndThisProp.SomeWantedNamespace;var myVar = closer.myVar;
    var myOtherVar = closer.myOtherVar;




    Composition over inheritance chain

    javascript inheritance problems



    • Many ways to do it wrong.
    • Developers try to enforce traditional inheritance on javascript prototypal inheritance.
    • Implement the super method in javascript is hard.
    • Deep prototype chains leads to slow lookups.


    so what is the answer?



    Modules

    Modules are easy to compose mix and handle.


    What are JavaScript modules?



    what are modules?


    MODULE definition

    Module is a piece of software that contains everything that is necessary to accomplish a particular task.


    A module is a part of a program. Programs are composed of one or more independently developed modules that are not combined until the program is linked. A single module can contain one or several routines.

    modularity


    WHY MODULES?


    • Separation of concerns.
    • Encapsulation.
    • Maintainability.
    • Testability.
    • Code reusability.
    • Easy to distribute.
    • Easy to understand.

    why javascript modules?



    • No global scope pollution.
    • Async loading.
    • Differed loading (just in time).
    • Private variables.
    • Faster scope lookups.
    • Easier script management.



    the old days

    Module pattern


    var counter = (function(){    var count = 0; //private    return {        get: function(){            return count;        },        add: function(){            return count++;        },        reset: function(){            count = 0;        }    };}());

    REVEALING module pattern

     var counter = (function(){
        var count = 0;    function get(){
            return count;
        }    function add(){
            return count++;
        }    function reset(){
            count = 0;
        }    function add2(){       return add(), add();    }    return {
            get: get,        add: add,        add2: add2,        reset: reset
        };}());

    pros

    • Better than nothing.
    • Create a separate scope.
    • Can create private variables.
    • Create separation of concerns.


    cons

    • Uses global scope for the return value.
    • Still needs Handle dependencies manually.
    • Each module instance duplicate all the methods


    AMD

    ASYNCHRONOUS MODULE DEFINITION




    not a javascript framework!


    Not related to the AMD the company


    definition 


    The Asynchronous Module Definition API specifies a mechanism for defining modules such that the module and its dependencies can be asynchronously loaded. 
    This is particularly well suited for the browser environment where synchronous loading of modules incurs performance, usability, debugging, and cross-domain access problems.


    AMD module API


     define(id?, dependencies?, factory*);






    *Not really a factory

    DEFINE A MODULE

     define('counterModule', function(){
        var count = 0;
        return {
            add: function(){
                return count++;
            },
            reset: function(){
                count = 0;
            },
            get: function(){
                return count;
            }
        }
     });



    module id is usually left empty

    using other modules

     define('viewModule', ['counterModule'], function(counter){
            var views = {};
        return {
            register: function(viewName, viewData){
               counter.add();            views[viewName] = viewData;        },        count: function(){             return counter.get();        }        ....
        };
    });

    returning a constructor

     define('counterModule', function(){    var staticCount = 0;    
        return function Counter() {       var count = 0;        return {
                add: function(){                staticCount++;                 return count++;
                },
                reset: function(){                staticCount -= count;                     count = 0;
                },
                get: function(){
                    return count;
                },            getGlobalCount: function(){                return staticCount;                        }
            };
    }});

    using a constructor in a module

     define('otherModule', ['counterModule'], function(Counter){
        
        var views = {};
        var counter = new Counter();     return {        ...
            register: function(viewName, viewData){
                counter.add();
                views[viewName] = viewData;
            },
            count: function(){
                return counter.get();
            }
            ...
        };
    });


    commonjs modules



    Still not 

    a FRAMEWORK 

    no BROWSER in mind


    • modules are not supported in browser because of the synchronous nature.*
    • There is not wrapper.
    • Designed to use in javascript servers.
    • No wrapper.

    Already in use in :

    Node js

    Titanium

    And more

    *there are ways to do it in the browser.


    common js Api


    require('module.name');exports.someApi = {};module;

    define a module


    exports.add = function(a, b){    return a + b;}exports.subtract = function(a, b){
        return a - b;
    }

    using other modules


    var Math = require('Math');Math.add(1, 2); // 3Math.subtract(1, 2); // -1

    exporting a constructor


    exports.myClass = function(){
        this.startTime = Date.now();
    }
    exports.myClass.prototype.elapsedTime = function(){
        return Date.now() - this.startTime;
    }

    CHANGING the exported module


    var someObject = {   someProperty: {}};
    var someVar = someObject.someProperty;someVar = function(){};
    console.log(someObject.someProperty); // {}

    Assign to the module.exports


    module.exports = function(){
        this.startTime = Date.now();
    }
    
    module.exports.prototype.elapsedTime = function(){
        return Date.now() - this.startTime;
    }


    More formats

    the Hybrid format

    define(function(require, module, exports){     var otherModule = require('otherModule');
    exports = { add:function(x, y){   return x + y; } }});

    Umd

    Universal module definition.
     (function (root, factory) {  if (typeof define === 'function' && define.amd) {
        // AMD
        define('myModule', ['pkg/A', 'pkg/B'], function (A, B) {
          return factory(A, B);
        });  } else if (typeof exports === 'object') {
      // Node.js
        module.exports = factory(require('pkg/A'), require('pkg/B'));  } else {
        // Browser globals
        root.myModule = factory(root.A, root.B);
      }}(this, function (B, B) {
      var myModule = {};
      return myModule;
    })); 


    lmd

    Lazy Module Declaration


    module loaders

    loaders



    requirejs

    about


    RequireJS is a JavaScript file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments


    this is 

    the framework






    makes your html sexy

    <!doctype html>
    <html>
        <head>
            <title>Sexy HTML</title>
    	<link rel="stylesheet" href="css/sexy.css"/>
        </head>
        <body>
    	<script data-main="main" src="require.js"></script>
        </body>
    </html>

    app bootstrap

    main.js
    require(['app'], function (app) {    app.start();}, function (err) {        require(['logger'], function (logger) {        logger.error('App Failed To Load Restarting!');        location.reload();    });
    });

    two globals story


    the REQUIRE function

    • Used to load top level resources.
    • Used to load scripts dynamically.

    the define function

    • Used to define modules.

    see the DIFFERENCe

    // define a module that load content dynamically
    define(['require', 'login', 'text!template'],
        function (require, login, template) {
    	
          login.open(template);
          login.onOK = function () {        //load some scripts lazily          require(['app'], function (app) {
    	  login.resolve('success');          app.start();        });
          }      //handle failed login here...      return login.promise();    }
    )


    amd! PLUGINS

    css, html, json, i18n, text, templates, images, files, coffeescript, typescript, ES6, and many more

    require paths

    requirejs.config({  enforceDefine : true,  baseUrl:'js/myApp'
      paths : {    handlebars:'lib/handlebars',    someModule:'modules/someModule',    jquery : [      'http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min',       'lib/jquery'    ]  }
    });




    shims

    Configure the dependencies, exports, and custom initialization for older "browser globals" scripts that do not use define()
    requirejs.config({
        shim: {        'app.drawCirclePlugin': ['app']        'drawSVGPlugin': {            deps: ['app'],            exports: 'drawSVG'        }
        }
    });

    require config map


    requirejs.config({
        map: {
            'some/newmodule': {            'dependency': 'dependency.version.2'        },        'some/oldmodule': {            'dependency': 'dependency.version.1'
    } } });

    Two require contexts


    var require1 = requirejs.config({  context: "version1",  baseUrl: "app/lib/version/1"});
    var require2 = requirejs.config({ context: "version2", baseUrl: "app/lib/version/2"});


    Build PROCESS for modules 



    wtf MY BUILD IS

    F5 OR CTRL+F5



    what are we building for?


    • Minify resources (javascript,html,css,images).
    • Concat files (gzip is happier).
    • Drink coffee.
    • Analyze code coverage.
    • Run our awesome test suite.
    • Find errors that we do not find.
    • Run fancy command line tools.

    r.js

    Builds your AMD moules.
     Runs on java, node, browser
    r.js -o path/to/build.js

    • Resolve Dependencies.
    • Combines related scripts together into build layers and minifies them.
    • Optimizes CSS by inlining CSS files referenced by @import and removing comments.

    what it CAN'T do?


    Yes
    define(['a', 'b'], function (a, b) {});
    require(['a', 'b']);
    No
    var mods = someCondition ? ['a', 'b'] : ['c', 'd'];
    require(mods);

    build Config


    ({
        baseUrl: ".",
        name: "main",
        out: "main-built.js",
        paths: {
            jquery: "empty:"
        }
    })


    harmony modules

    new keywords


    MODULE

    define a module

    import

    import modules using destructuring
     

    export

    export variables from a module


    es6 module


    module 'math' {
        export function sum(x, y) {
            return x + y;
        }
        export var pi = 3.141593;
    }


    Importing

    import { sum, pi } from "math";
    sum(pi,pi);

    more ways to IMPORT


    import $ from "jquery";import { encrypt, decrypt } from "crypto";
    import { encrypt as enc } from "crypto";

    loading harmony


    System.baseURL = '/lib';
    //load modules
    System.import(['js/test1', 'js/test2'], function(test1, test2) { console.log('test1.js loaded', test1); console.log('test2.js loaded', test2); }, function(err) { console.log('loading error'); });
    //load a script from urlSystem.load('js/libs/jquery-1.7.1.js', function() { var $ = System.global.jQuery; console.log('jQuery loaded', $); $('body').css({'background':'blue'}); });

    use them today

    Loader
    Transpailer 
    Grunt plugin

    Typescript


    demo/QUESTIONS?

    Javascript Modules

    By jsmaker

    Javascript Modules

    • 2,656