La programación modular es un patrón de programación JavaScript muy común. Generalmente puede hacer que el código sea más fácil de entender, pero hay muchas prácticas excelentes que no son bien conocidas.
Base
Primero describamos brevemente algunos patrones modulares desde que Eric Miraglia (el desarrollador de Yui) publicó por primera vez un blog hace tres años que describe patrones modulares. Si ya está muy familiarizado con estos modos modulares, puede omitir esta sección directamente y comenzar a leer desde "Modo avanzado".
Cierre anónimo
Esta es una estructura básica que hace que todo sea posible, y también es la mejor característica de JavaScript. Simplemente crearemos una función anónima y la ejecutaremos de inmediato. Todo el código se ejecutará dentro de esta función y vivirá en un cierre que proporciona privatización, lo cual es suficiente para permitir que las variables en estos cierres se ejecuten durante toda la vida de nuestra aplicación.
La copia del código es la siguiente:
(función () {
// ... Todos los var y funciones están solo en este alcance
// todavía mantiene el acceso a todos los globales
} ());
Tenga en cuenta los soportes más externos de este par envolviendo funciones anónimas. Debido a las características del lenguaje de JavaScript, esto es necesario para los soportes. Las declaraciones que comienzan con la función de palabras clave en JS siempre se consideran la función declarativa. Envuelva este código entre paréntesis para que el intérprete sepa que esta es una expresión de función.
Importación de variables globales
JavaScript tiene una característica llamada variables globales implícitas. No importa dónde se use un nombre de variable, el intérprete encontrará la declaración de Declaración VAR de la variable en reversa de acuerdo con la cadena de alcance. Si no se encuentra la Declaración de Declaración VAR, esta variable se considerará una variable global. Si esta variable se usa en una declaración de asignación y la variable no existe, se creará una variable global. Esto significa que es fácil de usar o crear variables globales en cierres anónimos. Desafortunadamente, esto hace que el código escrito sea extremadamente difícil de mantener, porque para los sentimientos intuitivos de las personas, es imposible saber de un vistazo que esas variables son globales.
Afortunadamente, nuestras funciones anónimas proporcionan soluciones simples. Simplemente pase la variable global como parámetro en nuestra función anónima, puede obtener un código más claro y rápido que la variable global implícita. Aquí hay un ejemplo:
La copia del código es la siguiente:
(función ($, yahoo) {
// ahora tiene acceso a Globals jQuery (como $) y Yahoo en este código
} (jQuery, yahoo));
Exportación de módulos
A veces no solo desea usar variables globales, sino que también desea declararlas para su uso repetido. Podemos hacer esto fácilmente derivándolos, a través del valor de retorno de las funciones anónimas. Hacerlo completará un modelo modular básico, y el siguiente será un ejemplo completo:
La copia del código es la siguiente:
var módulo = (function () {
var my = {},
privateVariable = 1;
function privateMethod () {
// ...
}
my.moduleProperty = 1;
my.modulemethod = function () {
// ...
};
devolver mi;
} ());
Tenga en cuenta que hemos declarado un módulo global llamado módulo, que tiene 2 propiedades públicas: un método llamado Module.ModulEmethod y una variable llamada Module.ModuleProperty. Además, mantiene un estado privado incorporado que utiliza cierres de funciones anónimas. Al mismo tiempo, podemos importar fácilmente las variables globales requeridas y usar este patrón modular como hemos aprendido antes.
Modo avanzado
La base descrita en la sección anterior es suficiente para lidiar con muchas situaciones, y ahora podemos desarrollar aún más este modelo modular para crear estructuras más potentes y escalables. Comencemos con el módulo del módulo e presentemos estos modos avanzados uno por uno.
Modo de zoom
Todo el módulo debe ser una limitación del modo modular en un archivo. Cualquiera que esté involucrado en un proyecto grande comprenderá el valor de dividir múltiples archivos con JS. Afortunadamente, tenemos una gran implementación para amplificar los módulos. Primero, importamos un módulo, le agregamos propiedades y finalmente lo exportamos. Aquí hay un ejemplo: ampliar el módulo original:
La copia del código es la siguiente:
var módulo = (function (my) {
my.anothermethod = function () {
// Método agregado ...
};
devolver mi;
}(MÓDULO));
Utilizamos la palabra clave VAR para garantizar la consistencia, aunque no es necesario aquí. Después de ejecutar este código, nuestro módulo ya tiene un nuevo método público llamado Module.anothermethod. Este archivo ampliado también mantendrá su propio estado privado incorporado y objetos importados.
Modo de zoom ancho
Nuestro ejemplo anterior requiere que nuestro módulo de inicialización se ejecute primero y luego se ejecute el módulo amplificado, y por supuesto, a veces esto puede no ser necesariamente necesario. Una de las mejores cosas que las aplicaciones de JavaScript pueden hacer para mejorar el rendimiento es ejecutar scripts de manera asincrónica. Podemos crear módulos multipartes flexibles y permitir que se carguen en cualquier orden a través de un modo zoom amplio. Cada archivo debe organizarse en la siguiente estructura:
La copia del código es la siguiente:
var módulo = (function (my) {
// Agregar capacidades ...
devolver mi;
} (Módulo || {}));
En este patrón, la expresión VAR la hace necesario. Tenga en cuenta que si el módulo no se ha inicializado, esta declaración de importación creará un módulo. Esto significa que puede usar una herramienta como LABJS para cargar todos sus archivos de módulos en paralelo sin ser bloqueado.
Modo de ampliación apretada
El modo de amplio acargado es muy bueno, pero también pondrá algunas limitaciones en su módulo. Lo más importante, no puede sobrescribir de manera segura las propiedades de un módulo. No puede usar propiedades de otros archivos al inicializar (pero puede usarlas cuando se ejecuta). El modo de ampliación apretada contiene una secuencia cargada y permite propiedades primordiales. Aquí hay un ejemplo simple (zoom en nuestro módulo original):
La copia del código es la siguiente:
var módulo = (function (my) {
var Old_moduleMethod = my.moduleMethod;
my.modulemethod = function () {
// anulación del método, tiene acceso a Old a través de Old_ModuleMethod ...
};
devolver mi;
}(MÓDULO));
Anulamos la implementación de Module.ModuleMethod en el ejemplo anterior, pero cuando es necesario, podemos mantener una referencia al método original.
Clon y herencia
La copia del código es la siguiente:
var module_two = (function (antiguo) {
var my = {},
llave;
para (clave en antigua) {
if (Old.HasownProperty (Key)) {
mi [clave] = antigua [clave];
}
}
var super_modulemethod = old.moduleMethod;
my.modulemethod = function () {
// anular el método en el clon, acceso a Super a través de super_modulemethod
};
devolver mi;
}(MÓDULO));
Este modelo es probablemente la opción más inflexible. Hace que el código se vea bien, pero eso tiene costo de flexibilidad. Como escribí anteriormente, si una propiedad es un objeto o función, no se copiará, pero se convertirá en la segunda referencia al objeto o función. Si modifica uno de ellos, modificará el otro al mismo tiempo (nota del traductor: ¡porque son simplemente uno!). Esto se puede resolver mediante un proceso de clonación recursiva, pero la clonación de funciones puede no resolverse, tal vez se pueda resolver con Eval. Por lo tanto, estoy diciendo que este método en este artículo solo tiene en cuenta la integridad del artículo.
Variables privadas de archivo cruzado
Hay una limitación significativa para dividir un módulo en múltiples archivos: cada archivo mantiene sus propias variables privadas y no puede acceder a variables privadas para otros archivos. Pero este problema se puede resolver. Aquí hay un ejemplo de un módulo amplio que mantiene variables privadas entre los archivos:
La copia del código es la siguiente:
var módulo = (function (my) {
var _private = my._private = my._private || {},
_Seal = my._seal = my._seal || función () {
eliminar my._private;
eliminar my._seal;
Eliminar my._unseal;
},
_Unseal = my._unseal = my._unseal || función () {
my._private = _private;
my._seal = _Seal;
my._unseal = _Unseal;
};
// Acceso permanente a _Private, _Seal y _Unseal
devolver mi;
} (Módulo || {}));
Todos los archivos pueden establecer propiedades en sus respectivas variables _Private, y entiende que pueden acceder a ellos por otros archivos. Una vez que se carga este módulo, la aplicación puede llamar al módulo._seal () para evitar llamadas externas a _private internas. Si este módulo debe volver a magnificarse, el método interno en cualquier archivo puede llamar _unseal () antes de cargar el nuevo archivo, y llamar _Seal () nuevamente después de ejecutar el nuevo archivo. Ahora uso este patrón en el trabajo y no lo he visto en ningún otro lugar. Creo que este es un modelo muy útil y vale la pena escribir un artículo sobre este modelo en sí.
Submódulos
Nuestro último modo avanzado es obviamente el más fácil. Hay muchos ejemplos excelentes de creación de submódulos. Es como crear un módulo normal:
La copia del código es la siguiente:
Módulo.sub = (function () {
var my = {};
// ...
devolver mi;
} ());
Aunque esto parece simple, creo que vale la pena mencionar aquí. Los submódulos tienen todas las ventajas avanzadas de los módulos generales, incluido el modo de amplificación y el estado de privatización.
en conclusión
Los modos más avanzados se pueden combinar para crear un modo más útil. Si realmente quiero recomendar un patrón modular para diseñar aplicaciones complejas, elegiría combinar el modo de amplificación amplio, variables privadas y submódulos.
No he considerado el rendimiento de estos modos, pero prefiero convertir esto en una forma de pensar más simple: si un modo modular tiene un buen rendimiento, puede hacer un gran trabajo al minimizarlo, haciendo que la descarga de este archivo de script sea más rápido. El uso del modo amplio de ampliación permite descargas paralelas simples sin bloqueo, lo que acelera las descargas. El tiempo de inicialización puede ser ligeramente más lento que otros métodos, pero vale la pena después de sopesar los pros y los contras. Mientras la importación de variables globales sea precisa, el rendimiento del tiempo de ejecución debe verse afectado, y también es posible lograr velocidades de ejecución más rápidas en submódulos acortando la cadena de referencia con variables privadas.
Como conclusión, aquí hay un ejemplo de un módulo infantil que se carga dinámicamente en su módulo principal (creándolo si el módulo principal no existe). Para simplificar, eliminé las variables privadas y, por supuesto, es muy simple agregar variables privadas. Este patrón de programación permite que una base de código de estructura jerárquica compleja se cargue en paralelo a través de submódulos.
La copia del código es la siguiente:
var util = (función (parent, $) {
var my = parent.AJAX = parent.AJAX || {};
my.get = function (url, params, llamado) {
// OK, así que estoy haciendo trampa un poco :)
devolver $ .getjson (URL, parámetros, devolución de llamada);
};
// etc...
regresar padre;
} (Util || {}, jQuery));
Este artículo resume las mejores prácticas actuales de "programación modular de JavaScript" y explica cómo ponerlo en práctica. Aunque este no es un tutorial principal, puede entenderlo solo una pequeña comprensión de la sintaxis básica de JavaScript.