Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this JavaScript code (module pattern for RequireJS and Node.js) work?

With my limited understanding of RequireJS and Node.js (plus JavaScript in general), I usually take a look at the source of some well-known JavaScript libraries. Every time I see something like this:

( // Wrapping
    function (root, factory) {
        if (typeof exports === 'object') { // Node.js

            var underscore = require('underscore');
            var backbone = require('backbone');

            module.exports = factory(underscore, backbone);

        } else if (typeof define === 'function' && define.amd) { // Require.JS

            define(['underscore', 'backbone'], factory);

        } 
    }(this, function (_, Backbone) { // Factory function, the implementation
        "option strict";

        function Foo() {}

        return Foo; // Export the constructor
    })
); // Wrapping

What I can understand (hopefully):

  • The anonymous function that wraps the code is automatically executed when the script is uncluded in a <script> tag
  • This code works with both RequireJS and Node.js (if checks in the very beginning); the result of factory function is either assigned to module.exports (Node.js) or used as argument of define function (RequireJS).

Q1: how this code works without RequireJS and Node.js? if and else if checks would fail, factory function is never executed and the scripts returns nothig.

Q2: what's the purpose of passing this as root argument? It's never used

like image 395
gremo Avatar asked Feb 08 '13 20:02

gremo


1 Answers

Actually I think the code snipped in your question will not work with browser globals. The pattern used in this snipped is called UMD - Universal Module Definition. In fact there are many variations of this pattern, you can browse more examples on https://github.com/umdjs/umd

As for the questions:

Q1 This snippet will not work in browsers without RequireJS or any other AMD loader, for obvious reasons - there only two checks - for the NodeJS and define function, so without using AMD library the factory function won't be called.

To make the factory function called just add another condition for browser globals

if (typeof exports === 'object') { // Node.js
    var underscore = require('underscore');
    var backbone = require('backbone');
    module.exports = factory(underscore, backbone);

} else if (typeof define === 'function' && define.amd) { // Require.JS
     define(['underscore', 'backbone'], factory);
} else {
    // Browser globals
    factory(root._, root.Backbone);
}

Note that we used root object passed to the wrapper function and as nekman pointed out it will be set to window in browser environment, so we just pass global objects defined on that window to the factory, these objects usually defined by other script tags on the page. Hope this answers your second question.

like image 178
Alexander Petrovich Avatar answered Oct 10 '22 03:10

Alexander Petrovich