[HTML/JS] Module pattern

Publicado 28/3/2011 17:31 por Eduard Tomàs i Avellana

Muy buenas! Cuando creas un sitio web, es normal que vayas añadiendo cada vez más código javascript en él. Al final, seguramente terminaréis desarollando una mini-api, propia que muchas veces reside en un archivo .js, lleno de funciones. Algo como:

function HazAlgo(id, params, callback)
{
}

function _HazAlgoHelper(domObj, params)
{
}

En este caso tenemos dos funciones, HazAlgo y _HazAlgoHelper. La función _HazAlgoHelper es realmente una función privada, es decir está pensada para ser llamada únicamente dentro de HazAlgo. Pero en Javascript no existe directamente el concepto de función privada así que le ponemos un idicador (que empieze por _) y asumimos que todas las funciones que empiecen por _, son privadas.

Como podemos ver, el código javascript no está muy “organizado”: tenemos todas las funciones juntas y además podemos ver aquellas que no deberíamos. El patrón de módulo existe para solventar esos problemas.

El patrón de módulo

La idea del patrón de módulo (module pattern) en Javascript es simular el concepto de una clase estática con métodos públicos y métodos privados. Lo de clase estática viene porque no quiero ir creando objetos de mi módulo, simplemente quiero tener ahí, las funciones públicas. Pero sin ver las privadas. Para los que no lo sepáis: en javascript no existe el concepto de variable estática.

La idea que hay detrás del patrón de módulo es muy simple, y como casi siempre pasa simple significa también brillante: Se trata de crear un objeto anónimo, cuyos campos públicos sean las funciones públicas. Finalmente se crea una instancia de ese objeto y se asigna al namespace global (eso es, al objeto window).

El código de base sería algo como:

(function(wnd) {
var miModulo = function() {
var _HazAlgoHelper = function(domObj, params) {alert("método privado");};
var _HazAlgo = function(id, params, callback) { _HazAlgoHelper(); alert("método público"}; };

return {
HazAlgo : _HazAlgo
};
}

wnd.m$ = miModulo();
})(window);

Bien… se que el código puede parecer lioso, la primera vez, pero vamos a analizarlo.

Podemos ver que estamos definiendo una función anónima que acepta un parámetro (llamado wnd).

¿Y que hace esa función anónima)? Pues para empezar define una variable miModulo que resulta ser… una función (var miModulo = funcion() { …}).

Y que hará esa función cuando se invoque? Pues tres cosas:

  1. Definir una variable llamada _HazAlgoHelper… que es otra función.
  2. Definir una variable llamada _HazAlgo… que es otra función.
  3. Devolver un objeto anónimo con un campo, llamado HazAlgo. El valor de HazAlgo es el mismo que _HazAlgo (es decir una función).

Con eso tenemos una función (miModulo) que cuando la invoquemos nos devolverá un objeto con un solo campo (llamado HazAlgo). Así, teoricamente:

miModulo.HazAlgo();    // Valido
miModulo._HazAlgo(); // No válido
miModulo._HazAlgoHelper(); // NO válido

Fijaos que la gracia está en que HazAlgo() realmente es lo mismo que _HazAlgo()… y desde _HazAlgo() podemos llamar sin ningún problema a _HazAlgoHelper() que es nuestra función privada. Así la clave es mapear las funciones públicas como campos del objeto anónimo devuelto.

¡Con eso hemos simulado el concepto de funciones privadas!

Pero, miModulo es una variable local, es local a la función anónima que estamos definiendo. Así que todavía nos queda un paso más: Guardar miModulo en alguna propiedad del parámetro wnd. Eso es lo que hacemos con la línea:

wnd.m$ = miModulo();

Invocamos miModulo (con parentesis, pues es una función) y guardamos el resultado (el objeto anónimo con el campo HazAlgo) en la propiedad m$ del parámetro wnd.

Ya casi estamos… Hasta este punto hemos definido simplemente una función anónima. Ahora toca invocarla. Fijaos en un detalle importante: la primera línea empieza con un paréntesis abierto. Con eso, de hecho, estamos invocando nuestra función anónima. Pero nuestra función anónima espera un parámetro (wnd) así que debemos pasárselo. Eso es lo que hacemos en la última línea: le pasamos window. Y porque window? Pues porque window es un namespace global: todo lo que esté en window es global.

Por lo tanto ahora, desde cualquier parte de mi página puedo hacer:

<script>m$.HazAlgo(1,2,3);</script>

Y eso es correcto, mientras que hacer.

<script>m$._HazAlgoHelper(1,2);</script>

Nos dará un error.

Para tener nuestro módulo listo para ser usado, basta con tenerlo en un .js propio e incluirlo en aquellas páginas donde lo necesitemos. ¡Y listos!

Espero que esto os parezca interesante y os ayude a organizar vuestro código javascript!

Un saludo!

Archivado en: ,
Comparte este post:

Comentarios

# Creando tu librería JavaScript con requirejs y gulp

Tuesday, July 15, 2014 11:15 AM by Burbujas en .NET

Si desarrollas aplicaciones web con alta carga de código en cliente, es posible que tengas que