2011-02-22 11 views
13

Estoy comenzando un nuevo proyecto y estoy revisando mis mejores prácticas para tratar de evitar cualquier problema, y ​​también para ver los malos hábitos que he tenido.Patrón para el patrón de módulo de Javascript y la inicialización del submódulo

No estoy muy contento con la forma en que estoy manejando las secuencias de inicialización en Javascript usando el patrón módulo/submódulo.

Digamos, mi código termina con algo como

FOO.init() 
FOO.module1.init() 
FOO.module2.init() 
FOO.module3.init() 
FOO.module4.init() 

en el ámbito global.

que estoy haciendo en esencia (comprobación de errores y detalles omittied por razones de brevedad):

var FOO = (function (me) { 
    me.init = function() { 
     for (var i in me.inits) { 
      me.inits[i](); 
     } 
    } 

    return me; 
}(FOO || {})); 

var FOO = (function (parent) { 
    var me = parent.module1 = parent.module1 || {}; 

    me.init = function() { 
    } 

    parent.inits.push(me.init); 

    return parent; 
}(FOO || {})); 

$(document).ready(FOO.init); 

para la inicialización.

Sé que he leído sobre esto antes, pero no puedo encontrar los términos de búsqueda adecuados para encontrar los artículos ahora. ¿Hay un patrón bien pensado y probado que maneje la inicialización en sitiation como este?

Gracias.

EDIT: Al volver a leer esto, creo que un pequeño contexto informará las respuestas.

En mi caso, cada módulo/submódulo está en su propio archivo. El módulo base define la funcionalidad básica del sitio, y los submódulos habilitan diferentes características. Por ejemplo, un submódulo puede cablear la autocompletación en un cuadro de búsqueda y otro puede convertir una imagen de encabezado estático en un banner rotativo. El CMS habilita/deshabilita los submódulos, por lo que realmente quiero divorciar las llamadas explícitas dentro del módulo base para que todo pueda ser gestionado por el CMS. También sé que hay formas específicas de CMS para lograr esto, pero busco un patrón de Javascript genérico para hacer esto para proporcionar consistencia y resuablidad entre proyectos que pueden usar un CMS diferente.

Respuesta

18

Personalmente, tengo un estilo de codificación diferente a eso. Este es uno de ellos. El otro es básicamente una imitación del estilo usado en backbone.js

var myProgram = (function() { 
    var someGlobal, someGlobal2; 

    var subModule1 = (function() { 
     ...  

     var init = function() { 

     }; 

     ... 

     init(); 

     return { 
      "someMethod": someMethod, 
      ... 
     }; 
    }()); 

    var OtherSubModule = (function() { 
     ... 
     var init = function(param) { ... }; 
     ... 
     return { 
      "init": init, 
      ... 
     }; 
    }()); 

    var init = function(param) { 
     ... 

     OtherSubModule.init({ 
      "foo": param.foo, 
      "bar": param.bar, 
      ... 
     }); 
    }; 


    return { 
     "init": init, 
     "somePublic": OtherSubModule.foobar, 
     ... 
    } 
}()); 

depende de si tengo que suministrar una API pública a otros usuarios, lo que hace la columna vertebral mucho mejor. Prefiero hacer módulos impulsados ​​por una función init para la configuración inicial y para el resto completamente accionados por eventos.

[Editar]

Teniendo en cuenta la cuestión editado Tengo un patrón diferente para esto. Cada archivo define una función en algún objeto en mi caso fue $.FooBar.plugins

(function() { 

    var foo = function() { ... }; 

    var bar = (function() { ... }()); 

    myNamespace.plugins["MyPlugin"] = function() { 

     ... do stuff 
     ... bind to evevnts 
    }; 

}()); 

A continuación, utilizamos una flejadora de arranque que fue algo como esto:

(function() { 

    var needed = function() { 
     // Feature detection 
    }; 

    var load = function() { ... }; 

    var getOptions = function() { 
     // Call something on a page by page basis. 
    }; 

    for (var plugin in pluginList) { 
     if (needed(plugin)) { 
       load(plugin, function() { 
        // get page specific options 
        var options = getOptions(); 
        // run plugin 
        myNameSpace.plugins[plugin](options); 
        // If all have been loaded trigger ready handlers 
        if (pluginCurrentCount == pluginCount) { 
         readyTrigger(); 
        } 
       }); 
       pluginCount++; 
     } 
    } 

    // start loading plugins after all have been counted 
    load.startLoading(); 

    var readyTrigger = function() { 
     // Run all ready handlers 
    } 

    // Implement your own DOM ready function to run when all plugins 
    // have loaded. 
    myNameSpace.ready = function(handler) { 
     if (isReady) { 
      handler(); 
     } else { 
      readyList.push(handler); 
     } 

    }; 
}()); 

Eso es un montón de vacíos y pseudo código, pero se debería tener la idea. Si no es obvio, siéntalo para cuestionarlo.

A continuación, en la página que tienen algo como esto

<html> 
<head> 
    <script type="text/javascript"> 

    var pageSpecific = { 
     "pluginName": { 
       "cssClass": "foobar", 
       "submitOnEnter": false, 
       ... 
     }, 
     ... 
    }; 

    </script> 
    <script src="bootstrapper.js" /> 
    ... 
</head> 
<body> 
    ... 
</body> 
</html> 
+0

Gracias. Mi código real está más cerca de tu segundo ejemplo, lo que me hace sentir mejor acerca de mi enfoque. Su ejemplo también me dio algunas ideas para mejorar también. – mpdonadio

0

estoy trabajando en un delegado pub/sub patrón que puede funcionar para el problema que parece describir aquí. Creo que el objetivo es no terminar repitiendo el código en nuestro sub-objects que ya hemos escrito en nuestros objetos principales, a menos que ese código sea completamente específico para ese módulo.

En general, creo que debemos alejarnos de esta idea de que debemos replicar el estilo de programación de "herencia clásica" que existe en otros idiomas, y simplemente aprovechar las capacidades que posee JavaScript de forma nativa. "Herencia" puede ser más sobre invocar propiedades y métodos en un contexto de ejecución (similar a como usamos .call() o .apply()), y no necesita crear una base de código masiva llena de módulos que "extienden" otros módulos, solo para terminar tratando ellos como objetos completamente aislados e individuales de todos modos.

Voy a poner pronto esto para usar en un proyecto más grande y ver cómo funciona cuando se trata de un código mucho más (advertencia: Sí, set propiedades en __proto__ objetos):

// Global automatic reference counting 
var referenceCount = 0; 

// Core constructor (a factory function for objects) 
function Class(obj) { 

    // Represents `this` (the invoking object) 
    var This = {}; 

    // Allow the inheritance of an object or object properties when `this` is defined 
    for(var k in obj) 
     This[k] = obj[k]; 

    // Basic `setter` method for `this` 
    This.publish = function(obj) { 
     for(var k in obj) 
      this[k] = obj[k]; 
    }; 

    // Basic `unsetter` method for `this` 
    This.unpublish = function(obj) { 
     if(obj in this) 
      delete this[obj]; 
     else 
      for(var k in obj) 
       if(k in this) 
        delete this[k]; 
    }; 

    // Allow `this` to subscribe to the updates of another object 
    This.subscribe = function(obj) { 
     this.__proto__ = obj; 
    }; 

    // Allow `this` to unsubscribe from the updates of another object 
    This.unsubscribe = function(obj) { 
     this.subscribe({}.__proto__); 
    }; 

    // Allow `this` to permanently consume a property from another object 
    This.plagiarize = function(obj) {  
     var key = Object.keys(obj); 
     var v, i = key.length; 
     while(i--) { 
      v = key[i]; 
      this[v] = obj[v][v]; 
     } 
    }; 

    // Apply a unique identifier which corresponds with the global reference counter (and increment it) 
    This.publish({ id: ++referenceCount }); 

    // return to `this` a newly constructed object 
    return This; 
} 

La idea aquí es que usted escriba una API (o módulo si lo desea) para un tipo de objeto. Por ejemplo, su módulo puede ser una "API de vista" que contiene todos los métodos que pueden preocupar a un objeto de vista. Usted escribe esos métodos una vez, y nunca más. Al escribir el código dentro de esos métodos como si el módulo fuera cualquier objeto de invocación futuro (usando this o un sustituto una vez definido para this), puede evitar volver a escribir o anular las propiedades y los métodos más adelante en la línea. Por lo tanto, esta API se convierte efectivamente en el delegate central (o puede llamarlo abstract class si lo desea) para cualquier objeto concreto futuro que pueda estar interesado en su funcionalidad (por ejemplo, vistas). Ya escribimos API centralizadas en el servidor que actúan como delegados para los datos (la mayoría de las API web); ¿Por qué no deberíamos extender esta misma filosofía al lado del cliente para organizar mejor nuestros puntos de vista, modelos del lado del cliente y otros objetos?

La principal diferencia entre este patrón de delegado y los patrones de módulos tradicionales es que con este patrón normalmente terminará con un módulo algo largo que se ve y se siente como una API con métodos que pueden producir resultados muy diferentes dependiendo de el objeto invocado (y por lo tanto el contexto de ejecución), en lugar de tener muchos módulos semidestilados que tienen una combinación de lógica, métodos y propiedades únicas que finalmente hacen que repitan el código de los demás. Con este patrón, probablemente verá mucho menos lógica conditional y nunca tendrá que volver a utilizar var self = this; esto es lo que lo convierte en un delegado abstracto en lugar de un módulo concreto. Los frameworks como Backbone casi logran esto, pero finalmente no lo hacen. Backbone es un marco para la "herencia de objetos" y la organización modular del código, y eso es genial, pero creo que el mayor valor que agrega es en realidad el hecho de que ha hecho que las personas acuerden "algún estándar" (pero podría decir lo mismo aproximadamente CoffeeScript; eww ...).

El aspecto final de este patrón toca su primer ejemplo. Al incluir los métodos de acceso básico y la funcionalidad pub/sub en el núcleo, podemos crear objetos que estén completamente sincronizados con sus padres cuando queramos y que tengan la capacidad de cortar caprichosamente sus relaciones, según sea necesario, si es necesario. He usado prototypes para lograr esto, pero probablemente podrías pensar en otra forma también (usando constructores).Creo que da como resultado una versión más verdadera de pub/sub, donde los editores no saben nada sobre sus suscriptores, y los suscriptores solo conocen a los editores cuando se suscriben y si se suscriben.

Para obtener más información al respecto, tengo un ejemplo más detallado y documentado, que describe algunos de los posibles casos de uso en una idea que he creado. Por supuesto, los nombres de las variables probablemente deberían cambiar a nombres que tengan más sentido en su proyecto. Usé estos nombres para expresar su propósito. Aquí está la esencia: https://gist.github.com/bennyschmidt/5069513

Estoy interesado en saber más acerca de otros patrones de diseño modular que todos ustedes han pensado. :>

+0

Dije que aplicaría el ejemplo a un proyecto: https://github.com/bennyschmidt/tileengine – Benny

Cuestiones relacionadas