Aplicación práctica de un espacio de nombres para este sitio

Supongamos que tenemos un módulo con 24 variables de diversos tipos y que la función hiciera algo con ellas, por ejemplo actualizando elementos del DOM. Supongamos que no devuelve nada, pues simplemente modifica el DOM y no tiene necesidad de devolver algo. Un ejemplo de un módulo así estaba en el archivo de este sitio canvas-clock.js que presenta un reloj con el elemento Canvas (ver el tema cómo se hace reloj con canvas). Podemos resumir el código que tenía ese canvas-clock.js para evidenciar la estructura:

//Variables del módulo
var ladoClock = 150;
var cnvClock = null;
var pararTimer = false;
//...siguen más variables
function lanzarClock(conLado) {
    //...Aquí configura el Canvas y lo presenta en
    //el DOM para mostrar un reloj con agujas...
    //Finaliza con el primer setTimeOut llamando a
    girarSegClock();
}
function girarSegClock(){
    if (pararTimer) return;
    timerSegClock = window.setTimeout(girarSegClock,1000);
    //...se lanza el Timeout para el siguiente segundo y
    //actualiza la posición de las 3 agujas en el canvas
}
function pararClock(){
    pararTimer = true;
}
function reanudarClock(){
    pararTimer = false;
    girarSegClock();
}

La estructura del módulo es muy sencilla. Las variables locales estaban ubicadas en Global, aunque tenían un caracter local o privado, pues no se usan en ningún otro módulo. Una función global o pública nos sirve para lanzar el reloj con la carga de la página desde el window.onload. Y una función local o privada para hacer girar las manecillas del reloj, aunque también estaba ubicada en Global. Por último habían dos funciones para parar y reanudar el reloj. Usando el concepto del Patrón lanzador o inicializador expuesto en el tema anterior, ahora ese módulo, resumiéndo su estructura, está así en este sitio tras aplicar los cambios necesarios:

//Asignación inicial a un objeto literal vacío o a si mismo
//si ya fue creado previamente.
var Wextensible = Wextensible || {};
//Envolvemos en el espacio de nombres
Wextensible.CANVAS_CLOCK_JS = function(){
    //Función principal del tipo inicializadora
    Wextensible.lanzarClock = (function(){
        //Un acortador para el módulo general. Si se referencia
        //en este módulo quedará en el closure
        var wxG = Wextensible.general;
        //Variables privadas del módulo
        var ladoClock = 150;
        var cnvClock = null;
        //...siguen más variables privadas y una función privada
        function girarSegClock(){
            timerSegClock = window.setTimeout(girarSegClock,1000);
            //...se lanza el Timeout para el siguiente segundo y
            //actualiza la posición de las 3 agujas en el canvas
        }
        //Devuelve la función lanzaddora o inicializadora del reloj
        return function(conLado) {
            //...Aquí configura el Canvas y lo presenta en
            //el DOM para mostrar un reloj con agujas...
            //Finaliza con el primer setTimeOut llamando a
            girarSegClock();
            //Retornamos métodos públicos
            return {
                parar: function(){
                    pararTimer = true;
                },
                reanudar: function(){
                    pararTimer = false;
                    girarSegClock();
                }
            };
        };
    })(); //cierra el módulo
}; //cierra el espacio 

Ignoramos por un momento la envoltura Wextensible.CANVAS_CLOCK_JS = function(){...} y nos centramos en su interior, donde envolvemos todo el módulo en una función autoejecutable y le asignamos al mismo nombre lanzarClock que tenía la función principal que lanza el reloj, función que ahora convertimos en anónima y es la que devolvemos. Esa función interior creará un closure salvaguardando las variables locales y la función local girarSegClock. Son ahora variables y funciones privadas del módulo. Lo único que es público es la función principal que inicia el módulo así como el objeto devuelto con los métodos públicos parar() y reanudar().

El nombre Wextensible es el que he elegido como espacio de nombres para las páginas. La función principal lanzarClock pasa a ser un método de Wextensible, que al auto-ejecutarse lo que hace es agregar el método lanzarClock() al objeto de ese espacio de nombres Wextensible.

El cargador de módulos

Volvamos ahora a la envoltura exterior Wextensible.CANVAS_CLOCK_JS = function(){...}. El módulo del ejemplo se encuentra en el archivo canvas-clock.js que se vincula a la página con <script src="/res/inc/canvas-clock.js" async></script>. Observe la carga asíncrona. El módulo se apoya en otro módulo llamado general.js. Este contiene variables y funciones de uso en el resto de las páginas del sitio. Debe estar disponible para todos los demás, lo que junto a la carga asíncrona de todos los .js nos lleva a iniciar la ejecución de los módulos desde el window.onload de una página. He realizado una página de ejemplo canvas-clock.html para desplegar el módulo canvas-clock.js, cuyo HTML resumido es el siguiente:

<!DOCTYPE html>
<html lang="es">
<head>
    ...
    <link rel="stylesheet" href="/res/sty/canvas-clock.css" />
    <script src="/res/inc/general.js" async></script>
    <script src="/res/inc/canvas-clock.js" async></script>
    <script>
        var wxG;
        window.onload = function(){
            wxG = Wextensible.general;
            wxG.cargarModulos(["canvas-clock.js"]);
            Wextensible.clock = Wextensible.lanzarClock(150);
            //Cargar módulos de terceros (no en este ejemplo)
            /*wxG.cargarScripts(["http://platform.twitter.com/widgets.js",
                "https://apis.google.com/js/plusone.js",
                "http://connect.facebook.net/es_LA/all.js#xfbml=1"]);*/
        };
    </script>
</head>
<body>
    ...
    <canvas>
        ...
    </canvas>
    <div>
        <input type="button" value="Parar"
        onclick="Wextensible.clock.parar()" />
        <input type="button" value="Reanudar"
        onclick="Wextensible.clock.reanudar()" />
    </div>
</body>
</html>
En Diciembre 2013 he llevado a cabo una extensa modificación para adaptar las páginas a dispositivos de menor tamaño y optimizar la carga. He cambiado de sitio el script inline que tiene el window.onload y los vínculos a archivos JS llevándolos al final de la página. Ver la nueva estructura.

Esta página de ejemplo carga los módulos general.js y canvas-clock.js. Puede consultar el código fuente en los enlaces del código anterior. El primero es imprescindible para cargar el resto de módulos, pues contiene funciones básicas y además la función cargarMódulos() que carga el resto de módulos. A modo de resumen general.js es un patrón módulo que se almacena en Wextensible.general. La variable wxG es un acortador para este módulo con la ventaja de escribir menos y no obligar a JavaScript a resolver la referencia en cada llamada. La función cargarModulos() es la siguiente:

cargarModulos: function(arrModulos){
    for (var i=0, max=arrModulos.length; i<max; i++) {
        var modulo = (arrModulos[i].replace(/[\-\.\/]/g, "_")).toUpperCase();
        Wextensible[modulo]();
    }
},

Cuando la página descarga asíncronamente el archivo general.js también se auto-ejecuta componiendo el objeto que contiene el método cargarMódulos(). El otro módulo canvas-clock.js es descargado pero no se auto-ejecuta, asignándose a Wextensible.CANVAS_CLOCK_JS = function(){...código de canvas-clock...}. Es importante entender que canvas-clock.js no puede auto-ejecutarse tras las descarga, pues debe esperar a que general.js esté cargado. Y con async en todos los módulos el orden de carga no queda determinado. Es posible que se descargue primero uno u otro. Lo que si es seguro es que con el window.onload todos los módulos ya han sido descargados. Es entonces cuando hacemos wxG.cargarModulos(["canvas-clock.js"]), donde convertimos todos los guiones intermedios y puntos en guiones bajos y pasamos todo a mayúsculas. Ahora simplemente hacemos Wextensible[modulo](). En ese momento se ejecuta el módulo canvas-clock.js tal como explicamos en el apartado anterior. Digamos que "hemos guardado" el código del módulo canvas-clock.js en Wextensible.CANVAS_CLOCK_JS para ejecutarlo en el window.onload.

Ventajas del cargador de módulos

Esto de que unos módulos se apoyen en otros es un condicional de carga que hay dos formas de resolverlos. O los vinculamos en el orden en que se necesiten en los <script src="..."></script> de forma síncrona. O los vinculamos en cualquier orden pero de forma asíncrona y luego procedemos a cargarlos en el window.onload. Las ventajas de esta última opción son varias, como por ejemplo, no tenemos que preocuparnos en que orden se descargan los módulos, algo que no siempre es muy fácil de saber cuando intervienen muchos. Un ejemplo de esto es la aplicación Generador de fractales con 11 scripts vinculados, todos asíncronamente:

<script src="/res/inc/general.js" async></script>
<script src="/res/inc/sliderwx.js" async></script>
<script src="/res/inc/colores-css-svg.js" async></script>
<script src="/res/inc/form-emerge.js" async></script>
<script src="/res/inc/general-color.js" async></script>
<script src="/res/inc/cubo-color.js" async></script>
<script src="/res/inc/paleta-color.js" async></script>
<script src="/res/inc/test-worker.js" async></script>
<script src="ejemplos/fractal-data.js" async></script>
<script src="/res/inc/fractal-escape.js" async></script>
<script src="/res/inc/fractal.js" async></script> 

Si se cargan síncronamente y alguno ejecuta algo que está en otro módulo hay que ubicarlos en el orden pertinente. Por ejemplo, paleta-color.js necesita colores-css-svg.js y general-color.js. Cargándolos asíncronamente y no realizando ninguna ejecución tal como mostramos más arriba almacenando todo el módulo en Wextensible.PALETA_COLOR_JS, luego cuando estemos en el window.onload si haremos la ejecución de forma ordenada con:

window.onload = function(){
    wxG = Wextensible.general;
    wxG.cargarModulos(["sliderwx.js", "colores-css-svg.js",
                       "general-color.js", "cubo-color.js",
                       "form-emerge.js", "paleta-color.js",
                       "test-worker.js", "fractal-escape.js",
                       "fractal-data.js", "fractal.js"
                       ]);
    ...
};

En resumen, la descarga asíncrona con este cargador de módulos no bloquea la carga de la página, pues la descarga se produce en paralelo sin auto-ejecuciones a excepción de general.js. Este si debe auto-ejecutarse para tenerlo disponible a la entrada del window.onload, aunque sólo contiene declaraciones de variables y funciones que, en ningún caso, interaccionan con el DOM ni con otros módulos. Ninguno del resto de lo módulos se auto-ejecutan, limitándose a cargar el código. Esto supone que el contenido HTML de la página se presentará en menor tiempo, postergando las ejecuciones de JavaScript para después de que la pagina se haya renderizado.

Esta misma idea la hacemos extensible a los módulos de terceros. En el ejemplo aparece comentado la llamada a wxG.cargarScripts(...) para la carga de los módulos de los botones "me gusta" de Facebook, Google+ y Twitter. No es el caso para ese ejemplo, pero si para las páginas de este sitio. Estos complementos sociales antes bloqueaban el renderizado pero ahora se ejecutan cuando el resto de los contenidos ya están presentados.

En Diciembre 2013 he llevado a cabo una extensa modificación para adaptar las páginas a dispositivos de menor tamaño y optimizar la carga. En las páginas actuales ya no cargo los scripts de terceros inicialmente. En el menú de la cabecera hay un botón para compartir en redes sociales con el que se lleva a cabo esa carga de botones del tipo "me gusta" sólo cuando el usuario lo precise. Ver nueva estructura minimalista para las páginas de este sitio.