Export module pattern
Asked Answered
A

1

8

I just started learning patterns in JavaScript, and getting used to writing JavaScript like this:

(function(window){
  var privateVar;
  var privateFunc = function(param){     
    //do something
  }

  return{
    publicFunc: function(){
    do something
  }

}(window));

But recently I found some scripts that write something like this in the beginning:

(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define('something', factory(root));
    } else if ( typeof exports === 'object' ) {
        module.exports = factory(root);
    } else {
        root.something = factory(root);
    }
})(window || this, function (root) {

      var privateVar;
      var privateFunc = function(param){     
        //do something
      }

      return{
        publicFunc: function(){
        do something
      }

});

So, what does this piece of code in the beginning mean? What is the difference between that and with this module export technique:

var MODULE = (function () {
    var my = {},
        privateVariable = 1;

    function privateMethod() {
        // ...
    }

    my.moduleProperty = 1;
    my.moduleMethod = function () {
        // ...
    };

    return my;
}());
Ankerite answered 25/11, 2014 at 18:16 Comment(3)
It's actually worth noting that the boilerplate code you have could be improved. window || this will fail when executed in a Web Worker (a feature in modern browsers). I think this || window would actually be better.Copolymer
A couple of other resources: davidbcalhoun.com/2014/what-is-amd-commonjs-and-umd and github.com/umdjs/umdCastellatus
@Copolymer - I see a lot of places (for example the links in my previous comment) that people only use this. Will that work in all cases nowadays?Castellatus
C
10

TL;DR: JavaScript modules are loaded in different environments (different module loading systems, or no proper module system at all). The code you have is some boiler-plate code that lets you load a module in these different environments correctly, in a clean way.

In more detail: the actual definition you give is a "factory function": a function that returns the module contents when evaluated. A factory function is a very flexible thing that can be used in a variety of ways.

Browser globals

This is essentially your third example. Here, the factory function is executed immediately, and assigned to a global variable:

var MyModule = (function () {
    // this is the factory function
})(); // execute immediately

The result is that other modules can reference this module by using the global variable - but this means you have to be careful to load all the modules in the correct order.

Asynchronous Module Definitions

Asynchronous Module Definition syntax is a pretty simple syntax, which provides a function called define() (spec here). This lets you describe modules by providing their dependencies and the factory function:

define('module-name', ['dep1', 'dep2'], function (dep1, dep2) {
    ...
});

So here, module-name is defined, but the factory function will only be executed when all the dependencies are loaded - this means that you can load the module definitions in any order, and the module loader is responsible for executing them all properly.

CommonJS

In CommonJS environments (such as Node.js, which runs in the command line or on a server), there is a global(-ish) object called module. Whatever you assign to module.exports is considered to be the value of the module.

If you want to use this with a factory function, it's pretty similar to the browser globals scenario, just that you assign it to module.exports:

module.exports = (function () {
    // this is the factory function
})(); // execute immediately

Option (d): All of the above

It's possible to detect which module loaders are available by inspecting the environment (e.g. typeof define and typeof module).

The code block at the top detects which module loader is available and uses the factory function with AMD, CommonJS or browser globals, depending which is available.

While you could in theory do this inline in your code, separating it out to the top is nice and neat.

Copolymer answered 25/11, 2014 at 18:21 Comment(2)
Hi @Copolymer I've update the question with another example, can you tell me what is the diffrent between the two? ThanksAnkerite
Oh, I see.. so there is just some checking for available module loader. If there is none, the module will be attached on window global object. Thank you very much. Accepted.Ankerite

© 2022 - 2024 — McMap. All rights reserved.