patterns - reveal pattern javascript
¿Qué es esta construcción de código que envuelve las partes de una biblioteca y para qué sirve? (6)
Imité una biblioteca y pude escribir el siguiente código. Este código creó ''c''
objeto ''c''
al que se asigna la función ''a''
. Entonces, para llamar ''a''
, tendré que escribir ca()
.
Además, pude agregar más funciones a este objeto ''c''
. Quiero entender lo que está pasando en este código. No parece la programación normal orientada a objetos. ¿Cómo se llama este tipo de programación javascript?
var c = (function(c) {
if (c === undefined) {
c = {};
}
function a() {
alert(1);
}
c.a = a;
return c;
}(c));
Es un patrón de módulo. Verá muchas variantes de ese patrón, por lo que es esencial comprender lo que realmente sucede, no puede simplemente imitar uno.
El punto de este fragmento de código es completar un objeto c
(normalmente su biblioteca global). Probablemente tenga muchas piezas de código similares en su aplicación, todas construyendo piezas de c
, probablemente cada una de ellas en su propio archivo.
En caso de que el objeto de biblioteca c
, que se pasa como argumento a la función, no exista todavía ( c === undefined
), se crea. Esto permite no depender del orden de ejecución o de un archivo pre-ejecutado.
La parte derecha de la asignación es un IIFE (Expresión de función inmediatamente invocada), que es una función a la que se llama inmediatamente. La ventaja de esta construcción es que crea un ámbito en el que las variables (por ejemplo, a
función) se pueden declarar sin contaminar el alcance externo (global). Aquí, el punto es discutible, ya que de todas formas se externaliza, pero un módulo generalmente depende de varias funciones y variables internas (privadas).
Un detalle que podría necesitar una explicación: todos esos archivos parecen que definen una nueva variable c
pero no hay problema aquí, incluso si los archivos están concatenados: las declaraciones var
no definen una nueva variable si ya existe (una variable es definido para todo el ámbito, aquí globalmente, incluso antes del punto de declaración).
Otra forma de escribir esto hubiera sido
var c = c || {}; // ensure the c variable is defined, and initialize its value it if necessary
(function() { // let''s use an IIFE to have a protected scope and not pollute the global one
function a() {
alert(1);
}
c.a = a; // let''s augment c
})();
Este es probablemente más claro como
- separa explícitamente los dos pasos (
c
inicialización yc
finalización utilizando un IIFE) - No depende de dos variables
c
con el mismo nombre. - es menos detallado
Este artículo escrito por Ben Cherry me ayudó a comprender este tipo de patrón: modules
Extracto del artículo:
Cierres anónimos
Esta es la construcción fundamental que lo hace todo posible, y realmente es la mejor característica única de JavaScript. Simplemente crearemos una función anónima y la ejecutaremos inmediatamente. Todo el código que se ejecuta dentro de la función vive en un cierre, lo que proporciona privacidad y estado durante toda la vida útil de nuestra aplicación.
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
Observe el ()
alrededor de la función anónima. Esto es requerido por el lenguaje, ya que las declaraciones que comienzan con la función de token siempre se consideran declaraciones de función. Incluir ()
crea una expresión de función en su lugar.
Importación global
JavaScript tiene una característica conocida como globales implícitas. Cuando se usa un nombre, el intérprete recorre la cadena de alcance en busca de una declaración var para ese nombre. Si no se encuentra ninguno, se asume que esa variable es global. Si se usa en una asignación, el global se crea si no existe. Esto significa que usar o crear variables globales en un cierre anónimo es fácil. Desafortunadamente, esto conduce a un código difícil de administrar, ya que no es obvio (para los humanos) qué variables son globales en un archivo dado.
Afortunadamente, nuestra función anónima proporciona una alternativa fácil. Al pasar globales como parámetros a nuestra función anónima, los importamos a nuestro código, que es más claro y más rápido que los globales implícitos. Aquí hay un ejemplo:
(function ($, YAHOO) {
// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));
Módulo de Exportación
A veces, no solo desea utilizar globales, sino que también desea declararlos . Podemos hacerlo fácilmente exportándolos, utilizando el valor de retorno de la función anónima. Al hacerlo, completarás el patrón básico del módulo, así que aquí tienes un ejemplo completo:
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
Observe que hemos declarado un módulo global llamado MODULE
, con dos propiedades públicas: un método llamado MODULE.moduleMethod
y una variable llamada MODULE.moduleProperty
. Además, mantiene el estado interno privado mediante el cierre de la función anónima. Además, podemos importar fácilmente los elementos globales necesarios, utilizando el patrón que aprendimos anteriormente
Este es el mismo código con comentarios agregados sobre lo que hace cada línea y lo que sucede cuando lo pasamos.
//Here, we''re defining a function that will return an object.
//its only parameter is named ''c''
//this is confusing, since the parameter has the same name as the global definition of the function.
//the var c definition is the global definition. the function parameter is not.
//so every reference to anything named ''c'' inside the function definition is local.
var c = (function(c)
{
//if c (the c we passed as a parameter) is not yet defined
if (c === undefined) {
//define c as an object
c = {};
}
//define a function
function a() {
alert(1);
}
//attach it to the object
c.a = a;
//return the object
return c;
}(c)); // call the constructor we just defined, using the global definition of `c`.
// the first time we pass this point, c as a variable is still undefined.
Lo que estás haciendo es declarar una función anónima y luego llamarlo con un parámetro llamado c
y asignarlo a una variable también llamada c
, lo cual es muy confuso :-)
Renombrando variables esto es lo que tienes:
var result=(function (input_parameter){...} (parameter_used_to_call_my_function));
La última (c)
que pregunta es el parámetro para llamar a la función. Es más fácil ver si usas una sintaxis más larga:
var my_function=function(input_parameter){...};
var result=my_function(result);
También vale la pena señalar que está llamando a my_function
con el result
(aunque lo llamó c
) como parámetro, que también es el nombre de la variable que acaba de crear para almacenar el valor de retorno de la función. JS está de acuerdo con esto porque no es estricto con la forma en que maneja las variables, pero esta es una forma confusa de escribir código. Está declarando una variable y pasándola (la variable con ese nombre, que en ese punto no debería existir) como un parámetro para su función en caso de que se declarara antes (y cuidando ese caso dentro de la función, que al menos es consistente).
Lo que sucede dentro de my_function
es que usted verifica si su parámetro tenía un valor anterior (lo que expliqué si el párrafo anterior); si no estaba definido, inicialícelo en un objeto vacío. Luego input_parameter
función a a input_parameter
y la devuelve.
No sé si hay un nombre para este tipo de programación, pero usar el mismo nombre de variable para diferentes cosas no parece una buena idea :-)
¿Por qué al final, escribimos (c) de nuevo?
Esto se llama IIFE .
(function(received argument){
//some code here.......
})(passed argument) //<-- here the function is invoked
c
es el argumento pasado La función se invoca inmediatamente cuando se crea. Este tipo de declaración se utiliza para mantener las variables privadas y para mantener limpio el espacio de nombres global.
El patrón en su código se utiliza para crear modules .
............ Ahora venga a su código: ............
si el argumento pasado no está definido: .............
En primer lugar, ...}(c))
esta parte de la función invocada inmediatamente se invoca. Se pasa con un argumento llamado c
que aún no está definido. (function(c){...
parte recibe este argumento c
.
Aquí, al principio, el passed argument c
no está definido. Por lo tanto, if(c==undefined)
se activa. En este punto, utilizando la declaración c={}
, al objeto indefinido c
se le asigna un empty object
.
Aquí la function a() { //... }
es un método privado creado dentro de un módulo. No se puede acceder globalmente.
El método privado a
está disponible globalmente al asignarlo a c usando ca=a
declaración. Por lo tanto, cuando el objeto regrese, puede llamar a este método en un contexto global.
De este modo, al objeto vacío c
creado recientemente se le asigna un método llamado. Luego, regresa y var c
recibe este objeto.
si el argumento pasado no es indefinido: ............
Pero si passed c
not undefined
es not undefined
, digamos, si se pasa un objeto, entonces no se crea ningún objeto nuevo. Quiero decir que if(c==undefined)
es falsy. Por lo tanto, no se ejecuta. Quiero decir que no se crea ningún objeto vacío nuevo .A continuación, al objeto Pasado se le asigna un nuevo método llamado a
usa ca=a
Es tan simple como eso.
El código a continuación es una versión mucho más simple de su código. Enviará automáticamente un objeto vacío si inicialmente no está definido. Por lo tanto, no tiene que tomarse la molestia de verificar si está indefinido o no. Se le llama aumento suelto.
var c = (function(c) {
function a() {
alert(1);
}
c.a = a;
return c;
}(c || {} ));
var c = (function(c) {
if (c === undefined) {
c = {};
}
function a() {
alert(1);
}
c.a = a;
return c;
}(c));
Vamos a ir paso a paso.
var c =
inicializando una variable llamada c. Tenga en cuenta que en este punto, si una variable denominada c ya está inicializada, c
se referirá a ese valor solo hasta que lleguemos al final de esta declaración.
( .... )
Esto significa que todo lo que está dentro debe ser tratado como una expresión.
function(c)
significa que esta "expresión" es una función que toma un argumento. De aquí en adelante, este argumento será referido por el nombre c
hasta que la función haya terminado. Cualquier variable con el nombre c
que fue declarada fuera del alcance de la función, por lo tanto, no sería accesible directamente aquí. Aunque si está en el ámbito global, y el ámbito global resulta ser el objeto de ventana, puede denominarse window.c
.
if (c === undefined) { ... }
comprueba si el argumento pasado a él es indefinido o no. Devolvería verdadero si no está definido, y así ejecutaría lo que haya dentro del bloque if.
c = {}
establece la variable c
en un objeto vacío. Entonces, si el argumento fue (pasado o) indefinido, lo definimos nosotros mismos aquí (y lo definimos como un objeto vacío ...).
function a() { alert(1); }
declaró una función con el nombre de a
llamada que dará como resultado la alerta del número 1. Tenga en cuenta que esto es solo una declaración de función. Aún no hemos llamado a la función.
ca = a
El argumento c
ahora tiene asignada una propiedad llamada a
que se refiere a la función que acabamos de crear.
return c
rompa la función con el valor de retorno como el valor final de c
, después de experimentar los cambios que hicimos en el argumento pasado.
(fun...}(c))
llame a la función que acabamos de crear, y pase a ella como argumento el valor actual de c
. ya que llamamos a la función como una expresión, el resultado de esta expresión sería el valor de retorno de la función. Y nuestra función devuelve un objeto (que le pasamos), después de asignarle una propiedad.
Dado que esta expresión se equipara a la variable c
, el valor de retorno de la expresión (que es el valor de retorno de la función), ahora se mantiene con la variable c
.
Si leyera todo esto correctamente, sabría que la variable c
ahora contendría un objeto, que tendría una propiedad a
que es una función que alerta al número 1. Y ese objeto también conserva las propiedades que podría haber tenido antes.
Si desuglicamos este código para hacerlo legible simplemente haciendo que los nombres de las variables sean descriptivos:
var myObject = (function(someObject) {
if (someObject === undefined) {
someObject = {};
}
function alertOne () {
alert(1);
}
someObject.alertOne = alertOne;
return someObject;
}(myObject));
Este programa es un episodio del módulo de la serie que revela un patrón, que nos dice cómo agregar propiedades adicionales a un objeto, que se declaró anteriormente, de una manera elegante sin contaminar el alcance global. Protagonizar las expresiones de función invocadas de inmediato (IIFEs).