parametro - ¿Cuáles son los beneficios de utilizar funciones anónimas en lugar de funciones con nombre para las devoluciones de llamada y parámetros en el código de evento de JavaScript?
funciones callback javascript (5)
Bueno, solo para ser claro por el bien de mis argumentos, las siguientes son todas funciones anónimas / expresiones de funciones en mi libro:
var x = function(){ alert(''hi''); },
indexOfHandyMethods = {
hi: function(){ alert(''hi''); },
high: function(){
buyPotatoChips();
playBobMarley();
}
};
someObject.someEventListenerHandlerAssigner( function(e){
if(e.doIt === true){ doStuff(e.someId); }
} );
(function namedButAnon(){ alert(''name visible internally only''); })()
Pros:
Se puede reducir un poco de cruft, particularmente en funciones recursivas (donde se podría (en realidad, ya que arguments.callee está en desuso) seguir usando una referencia nombrada según el último ejemplo internamente), y deja en claro que la función solo se activa en este caso lugar.
Ganancia de legibilidad de código: en el ejemplo del objeto literal con funciones anónimas asignadas como métodos, sería una tontería agregar más lugares para cazar y buscar lógica en el código cuando el objetivo de ese objeto literal es desplegar alguna funcionalidad relacionada en el mismo lugar convenientemente referenciado. Sin embargo, al declarar métodos públicos en un constructor, tiendo a definir funciones etiquetadas en línea y luego asignar como referencias de this.sameFuncName. Me permite usar los mismos métodos internamente sin el ''esto''. crumble y hace que el orden de definición no sea una preocupación cuando se llaman entre sí.
Útil para evitar la contaminación innecesaria del espacio de nombres global; sin embargo, los espacios de nombres internos nunca deberían ser tan extensamente ocupados o manejados por múltiples equipos simultáneamente, por lo que ese argumento me parece un poco tonto.
Estoy de acuerdo con las devoluciones de llamada en línea cuando establezco manejadores de eventos cortos. Es una tontería tener que buscar una función de línea 1-5, especialmente porque con JS y elevación de función, las definiciones podrían terminar en cualquier lugar, ni siquiera dentro del mismo archivo. Esto podría suceder por accidente sin romper nada y no, no siempre tienes el control de esas cosas. Los eventos siempre dan como resultado el disparo de una función de devolución de llamada. No hay ninguna razón para agregar más enlaces a la cadena de nombres que necesita escanear para realizar ingeniería inversa de controladores de eventos simples en una gran base de código y la preocupación de seguimiento de la pila puede abordarse mediante la extracción de desencadenantes de eventos en métodos que registran información útil al depurar el modo está encendido y dispara los disparadores. De hecho, estoy empezando a construir interfaces completas de esta manera.
Útil cuando QUIERES que el orden de la definición de función importe. A veces quieres estar seguro de que una función predeterminada es lo que piensas que es hasta cierto punto en el código donde está bien redefinirlo. O quieres que la rotura sea más obvia cuando las dependencias se barajan.
Contras:
Las funciones de Anon no pueden aprovechar la función de elevación. Esta es una gran diferencia. Tiendo a tomar una gran ventaja de izar para definir mis propios funcs y constructores de objetos explícitamente nombrados hacia la parte inferior y llegar a la definición del objeto y al tipo de bucle principal en la parte superior. Creo que hace que el código sea más fácil de leer cuando nombra bien a sus vars y obtiene una visión amplia de lo que está sucediendo antes de ctrl-Fing para obtener detalles solo cuando son importantes para usted. El levantamiento también puede ser una gran ventaja en las interfaces impulsadas por eventos donde la imposición de un orden estricto de lo que está disponible cuando puede morderte el trasero. El izamiento tiene sus propias advertencias (como el potencial de referencia circular) pero es una herramienta muy útil para organizar y hacer que el código sea legible cuando se usa correctamente.
Legibilidad / Depuración. Absolutamente se usan demasiado a veces y puede hacer que la depuración y la legibilidad del código sean una molestia. Las bases de código que dependen en gran medida de JQ, por ejemplo, pueden ser un PITA serio para leer y depurar si no encapsula de manera sensata los argumentos casi inevitables y excesivamente pesados y sobrecargados del $ soup. El método de vuelo estacionario de JQuery, por ejemplo, es un ejemplo clásico de uso excesivo de funciones anónimas cuando se sueltan dos funciones anónimas, ya que es fácil para un principiante suponer que es un método de asignación de detector de eventos estándar en lugar de un método sobrecargado para asignar manipuladores para uno o dos eventos.
$(this).hover(onMouseOver, onMouseOut)
es mucho más claro que dos funcs anon.
Soy nuevo en JavaScript. Entiendo muchos de los conceptos del lenguaje, he estado leyendo sobre el prototipo de modelo de herencia, y estoy abriendo el camino con más y más elementos interactivos para el front-end. Es un lenguaje interesante, pero siempre estoy un poco apagado por el espagueti de devolución de llamada que es típico de muchos modelos de interacción no triviales.
Algo que siempre me ha parecido extraño es que a pesar de la pesadilla de legibilidad que es un nido de devoluciones de llamada anidadas por JavaScript, lo que rara vez veo en muchos ejemplos y tutoriales es el uso de funciones con nombre predefinidas como argumentos de devolución de llamada. Soy un programador de Java de día, y descarto los estereotipos sobre los nombres de Enterprise-y para las unidades de código. Una de las cosas que he llegado a disfrutar sobre trabajar en un lenguaje con una fuerte selección de IDE con características es que el uso significativo, si son largos, los nombres pueden hacer que la intención y el significado del código sean mucho más claros sin que sea más difícil ser realmente productivo. Entonces, ¿por qué no utilizar el mismo enfoque al escribir código JavaScript?
Pensándolo bien, puedo proponer argumentos que son a favor y en contra de esta idea, pero mi ingenuidad y novedad en el lenguaje me impiden llegar a conclusiones sobre por qué esto sería bueno a nivel técnico.
Pros:
- Flexibilidad. Se podría llegar a una función asíncrona con un parámetro de devolución de llamada mediante una de muchas rutas de código diferentes y podría ser necesario tener que escribir una función nombrada para tener en cuenta cada caso de borde posible.
- Velocidad. Juega fuertemente en la mentalidad de los hackers. Atornillar cosas hasta que funcione.
- Todos los demás lo están haciendo
- Tamaños de archivo más pequeños, aunque triviales, pero cada bit cuenta en la web.
- AST más simple? Asumiría que las funciones anónimas se generan en tiempo de ejecución y, por lo tanto, el JIT no se amotinará al asignar el nombre a las instrucciones, pero estoy adivinando en este punto.
- ¿Envío más rápido? No estoy seguro acerca de este tampoco. Adivinando de nuevo.
Contras:
- Es horrible e ilegible
- Esto se suma a la confusión cuando estás anidado en lo más profundo de un pantano de callbacks (lo cual, para ser justos, probablemente significa que estás escribiendo un código mal construido, pero es bastante común).
- Para alguien sin un trasfondo funcional puede ser un concepto extraño para grok
Con tantos navegadores modernos que muestran la capacidad de ejecutar código JavaScript mucho más rápido que antes, no estoy viendo cómo cualquier tipo de ganancia de rendimiento trivial que uno pueda obtener mediante devoluciones de llamada anónimas sería una necesidad. Parece que, si se encuentra en una situación donde el uso de una función nombrada es factible (comportamiento predecible y ruta de ejecución), entonces no habría ninguna razón para no hacerlo.
Entonces, ¿hay algún motivo técnico o problema que desconozco que hace que esta práctica sea tan común por algún motivo?
Es más legible usando funciones nombradas y también son autorreferenciales como en el ejemplo a continuación.
(function recursion(iteration){
if (iteration > 0) {
console.log(iteration);
recursion(--iteration);
} else {
console.log(''done'');
}
})(20);
console.log(''recursion defined? '' + (typeof recursion === ''function''));
Esto es útil cuando desea tener una función invocada de forma inmediata que hace referencia a sí misma pero que no agrega al espacio de nombres global. Todavía es legible pero no contaminante. Ten tu pastel y cómelo.
Hola, mi nombre es Jason O hola, mi nombre es ??? usted escoge.
Prefiero funciones nombradas, pero para mí todo se reduce a una pregunta:
¿Usaré esta función en otro lugar?
Si la respuesta es sí, la nombro / defino. Si no, páselo como una función anónima.
Si solo lo usa una vez, no tiene sentido aglomerar el espacio de nombres global con él. En los frontales complejos de hoy en día, la cantidad de funciones nombradas que podrían haber sido anónimas crece rápidamente (fácilmente más de 1000 en diseños realmente intrincados), lo que resulta en ganancias de rendimiento (relativamente) grandes al preferir funciones anónimas.
Sin embargo, la mantenibilidad del código también es extremadamente importante. Cada situación es diferente. Si no estás escribiendo muchas de estas funciones para empezar, no hay daño en hacerlo de cualquier manera. Depende de tu preferencia.
Otra nota sobre los nombres. Tener el hábito de definir nombres largos realmente afectará el tamaño de su archivo. Toma el siguiente ejemplo.
Asuma que ambas funciones hacen lo mismo:
function addTimes(time1, time2)
{
// return time1 + time2;
}
function addTwoTimesIn24HourFormat(time1, time2)
{
// return time1 + time2;
}
El segundo te dice exactamente lo que hace en el nombre. El primero es más ambiguo. Sin embargo, hay 17 caracteres de diferencia en el nombre. Supongamos que la función se llama 8 veces en todo el código, es decir, 153 bytes adicionales que su código no necesitaba. No es colosal, pero si es un hábito, extrapolar eso a 10s o incluso 100s de funciones significará fácilmente unos pocos KB de diferencia en la descarga.
Nuevamente, sin embargo, la mantenibilidad debe sopesarse con los beneficios del rendimiento. Este es el dolor de lidiar con un lenguaje guionado.
Un poco tarde para la fiesta, pero algunos aspectos aún no mencionados a funciones, anónimos o de lo contrario ...
Los funcionamientos de Anon no se mencionan fácilmente en las conversaciones humanoides sobre el código, entre un equipo. Por ejemplo, "Joe, ¿podrías explicar qué hace el algoritmo, dentro de esa función ... cuál es la función anónima número 17 dentro de la función fooApp ...? ¡No, ese no! ¡El 17 °!"
Los funcs de Anon son anónimos para el depurador también. (¡duh!) Por lo tanto, el seguimiento de la pila del depurador generalmente solo mostrará un signo de interrogación o similar, lo que lo hará menos útil cuando haya establecido varios puntos de interrupción. Llegas al punto de interrupción, pero te encuentras desplazando la ventana de depuración hacia arriba / abajo para descubrir dónde diablos estás en tu programa, porque bueno, la función de signo de interrogación simplemente no lo hace.
Las preocupaciones sobre la contaminación del espacio de nombres global son válidas, pero se pueden remediar fácilmente nombrando sus funciones como nodos dentro de su propio objeto raíz, como "myFooApp.happyFunc = function (...) {...};".
Las funciones que están disponibles en el espacio de nombres global, o como nodos en su objeto raíz como se indicó anteriormente, pueden invocarse directamente desde el depurador, durante el desarrollo y la depuración. Por ejemplo, en la línea de comandos de la consola, haz "myFooApp.happyFunc (42)". Esta es una habilidad extremadamente poderosa que no existe (de forma nativa) en los lenguajes de programación compilados. Prueba eso con un func anon.
Los funcionamientos de Anon se pueden hacer más legibles asignándolos a una var, y luego pasando la var como devolución de llamada (en lugar de en línea). Por ejemplo: var funky = function (...) {...}; jQuery (''# otis''). clic (funky);
Usando el enfoque anterior, podría agrupar varios funcionamientos anónimos en la parte superior del func parental, y debajo de eso, la esencia de los enunciados secuenciales se agrupa mucho más estrechamente y es más fácil de leer.
Uso funciones anónimas por tres razones:
- Si no se necesita un nombre porque la función solo se llama en un solo lugar, entonces ¿por qué agregar un nombre al espacio de nombres en el que se encuentre?
- Las funciones anónimas se declaran en línea y las funciones en línea tienen ventajas, ya que pueden acceder a las variables en los ámbitos principales. Sí, puedes ponerle un nombre a una función anónima, pero eso no tiene sentido si se declara en línea. Así que en línea tiene una ventaja significativa y si lo haces en línea, hay pocas razones para ponerle un nombre.
- El código parece más autónomo y legible cuando los manejadores se definen dentro del código que los llama. Puede leer el código casi de forma secuencial en lugar de tener que buscar la función con ese nombre.
Intento evitar la anidación profunda de funciones anónimas porque puede ser difícil de entender y leer. Normalmente, cuando eso sucede, hay una mejor manera de estructurar el código (a veces con un bucle, a veces con una tabla de datos, etc.) y las funciones nombradas no suelen ser la solución allí.
Supongo que agregaría que si una devolución de llamada comienza a tener más de 15-20 líneas de longitud y no necesita acceso directo a las variables en el ámbito principal, me sentiría tentado de darle un nombre y dividirlo en su propia función llamada declarada en otro lugar. Definitivamente hay un punto de legibilidad donde una función no trivial que se alarga es más fácil de mantener si se coloca en su propia unidad con nombre. Pero la mayoría de las devoluciones de llamadas con las que termino no son tan largas y me resulta más legible mantenerlas en línea.