Modulos

Introducción

Nos ayudan a estructurar el código. Espondremos únicamente aquellos elementos del código que nos interesen. Evitaremos contaminar el espacio de nombres global.

JavaScript no trae nada de fábrica para construir módules.

Si existe una base de datos online con módulos compartidos: http://npmjs.org que controla, además, versiones y dependencias.

Espacio de nombres

Dado que las funciones son los únicos elementos en JavaScript que crean un nuevo espacio de nombre, será lo que usemos para dar la separación de espacio de nombres:

> var mimodulo = function () {
     var name="jose";
     return function (cadena) {console.log(cadena + name)}
     }();
undefined
> mimodulo
[Function]
> mimodulo("Mi nombre es: ")
Mi nombre es: jose
undefined

Lo siguiente aisla nuestro código del mundo exterior:

> ( f() {...<todo el código>...} )()

Nota

Why did we wrap the namespace function in a pair of parentheses? This has to do with a quirk in JavaScript’s syntax. If an expression starts with the keyword function, it is a function expression. However, if a statement starts with function, it is a function declaration, which requires a name and, not being an expression, cannot be called by writing parentheses after it. You can think of the extra wrapping parentheses as a trick to force the function to be interpreted as an expression.

Objetos como Interfaces

Si en lugar de devolver una función anónima, devolvemos un objeto con métodos:

> var mimodulo = function () {
     var name="jose";
     return {
       printName: function (str) {console.log(str + name)},
       printToday: function () {console.log(new Date}
       }
     }();
undefined
> mimodulo.printName("Mi nombre: ")
Mi nombre: jose
> mimodulo.printToday()
Sun Aug 10 2014 19:23:28 GMT+0200 (CEST)

No “mola” el devolver todo el objeto al final de la función. Como alternativa, usamos por convenio el objeto exports (patrón muy común en browsers):

> (function(exports) {
    var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
                 "Thursday", "Friday", "Saturday"];

    exports.name = function(number) {
      return names[number];
    };
    exports.number = function(name) {
      return names.indexOf(name);
    };
  })(this.weekDay = {});
> weekDay.name(2)
'Tuesday'

De esta forma, sólo se añade al objeto que se pasa como parámetro (en este caso, “this.weekDay” que contiene “{}” se pasa a “exports”).

Nota

dado que “this.weekDay” está fuera de la función, “this” hará referencia al scope global.

Ejemplo

var weekDay = function() {
var names = [“Sunday”, “Monday”, “Tuesday”, “Wednesday”,
“Thursday”, “Friday”, “Saturday”];
return {

name: function(number) { return names[number]; }, number: function(name) { return names.indexOf(name); }

};

}();

There are two popular, well-defined approaches to such modules. One is called CommonJS Modules, and revolves around a require function that fetches a module by name and returns its interface. The other is called AMD, and uses a define function that takes an array of module names and a function, and, after loading the modules, runs the function with their interfaces as arguments.

CommonJS

Viene incluido en nodejs e implementan una función require. Permite importar directamente el código sin la necesidad de “envolverlo”. Sólo se exporta aquello que se añade al objeto exports.

Este método se podría usar en el navegador, pero con cada require se quedaría la página esperando a que cargase del servidor remoto: LENTO. Existe Browserify que descarga la información de fuera. Coge lo que necesita y lo guarda en un único fichero.

AMD (Asynchronous Module Definition)

Carga las dependencias de fondo:

define([module_names_dependencies], function (module_names_dependencies) {...})

define(["weekDay", "today"], function(weekDay, today) {
  console.log(weekDay.name(today.dayNumber()));
});

Nota

define carga las dependencias si todavía no se han cargado.

Por ejemplo RequireJS.

Nota

siempre que se pueda, diseñar funciones puras y sencillas.