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

share|improve this question
1  
Are you sure that it does work without RequireJS or Node.js? My limited understanding of JavaScript (and this possibly wrong) JSFiddle makes me think you can't access Foo() – Jason Sperske 17 hours ago
@nekman Ahh I see it assumes at least Backbone is available. That's smart – Jason Sperske 17 hours ago
@JasonSperske not sure at 100%, but look at nekman answer... – Gremo 17 hours ago
feedback

2 Answers

up vote 3 down vote accepted

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.

share|improve this answer
+1, thanks for the UMD link! As I said I'm learning about JavaScript and what I can't understand is how one can access Foo constructor when not using a Node.js or Require.JS. The only way is to write root.Foo = factory(root._, root.Backbone), right? – Gremo 16 hours ago
Actually I've found the answer to my comment here: github.com/umdjs/umd/blob/master/returnExports.js – Gremo 16 hours ago
Yes, this is the way to do it, you just return your object (Foo) and if we are in browser we should add it to the root (which is window) so that other modules can see it. – Alexander Petrovich 16 hours ago
feedback

Q1: If both the if and the else if fails, the only thing to assume is that underscore and Backbone is loaded from a <script> tag. For a while ago, I added a commit to the Backbone.localStorage plugin that did the same assumption.

Q2: The this will point to the "global object" (window in a browser environment and global in a Node.js environment). In your case, it isn't used and do not need to be passed in. The factory alone would be enough.

share|improve this answer
1  
For Q1: I can't understand the lines that are being executed when the if and else if fail (i.e. browser). Can you explain that? How can I access Foo()? – Gremo 17 hours ago
1  
Yes, that is correctly spooted! If both fails, then there should be a: else { factory(_, Backbone); } to access the factory and Foo. – nekman 17 hours ago
Even with return factory(_, Backbone) I can't figure out how, when including the script with <script> tag, one can access Foo... – Gremo 17 hours ago
feedback

Your Answer

 
or
required, but never shown
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.