2. Modular Programming in Javascript

Javascript has no built in method to include Javascript files (yet). The most basic way to include javascript files is to add the file in the html file with the script tag.

<html>
<head>
    <title>Basic Example</title>
    <script type="text/javascript" src="moduleA.js"></script>
    <script type="text/javascript" src="moduleB.js"></script>
    <script type="text/javascript" src="moduleC.js"></script>
</head>
</body>
</html>

The moduleB script would be dependent on moduleA script. ModuleC is dependent on moduleB and moduleA and so on. This is not a very sane method. Writing modules this way would require you to remember the order of scripts. Luckily, there are libraries that allow a better style of writing modules.

The most popular style of writing modules currently is CommonJS and AMD (Asynchronous Module Definition). Bento game engine uses RequireJS, an implementation of AMD. We will focus on how to write modules for Bento.

A module are defined using a define function. This is what the anatomy of a module defintion looks like:

define('name of the module', [/*array of dependencies*/], function (/*modules*/) {
  // Your amazing module code goes here!
  var abc; // etc..
  return abc;
});

define is a function that takes as parameters: 

  • A name (string). What should you use as module name? It is recommended to use the path to the javascript file as the module name.
  • An array of dependencies (array of strings). If this module depends on other modules, write an array of names here.
  • A callback function. The return of this function is your module. The parameters in this function are modules of the dependencies, in the same order as the array of dependencies.

The module will live in its own closure. You should return some kind of export so that the module can be used in other modules.
If you only *need* a module, use a require(). It has the same parameters except you don't need to name it:

require([/*array of dependencies*/], function (/*modules*/) {
    // do your thing here
});

In the Bento game engine, you should use bento.require() and bento.define(). (External libraries may define require() and define() as something else, so bento caches the RequireJS require and define)

Examples of writing modules of your own:

// singular objects
bento.define('mySingleton', ['bento'], function (Bento) {
  var singleton = {
    log: function () {
      console.log('hello!');
    };
  };
  return singleton;
});

// usage:
bento.require(['mySingleton'], function (MySingleton) {
  MySingleton.log();
});
// factories ("class" like functions)
bento.define('myClass', ['bento/entity'], function (Entity) {
  // note how this returns a function
  return function () {
    var timer = 0, // private
        entity = Entity(); // entities will be explained in chapter 4
    entity.attach({
      update: function () {
        timer += 1;
      }
    });
    return timer;
  };
});

// usage:
bento.require(['myClass'], function (MyClass) {
  // instantiate 2 objects
  var obj1 = MyClass(),
      obj2 = MyClass();
});