standard - Javascript: función en línea vs funciones predefinidas
javascript standard global variables (13)
¿Puede algún cuerpo arrojarme algunos argumentos para usar funciones en línea contra pasar el nombre predefinido de la función a algún controlador?
Es decir, que es mejor:
(function(){
setTimeout(function(){ /*some code here*/ }, 5);
})();
versus
(function(){
function invokeMe() {
/*code*/
}
setTimeout(invokeMe, 5);
})();
Una pregunta extraña, pero casi estamos peleando en el equipo sobre esto
¿No podemos llevarnos todos bien?
(function(){
setTimeout( (function InvokeMe(){ /*some code here*/ }), 5);
})();
Solo una cosa realmente importa, la OMI y es fácil de depurar. Muchos trazadores de pasos no podrán decirle nada sobre el func más que el hecho de que era anónimo y tenía args, pero aún puede definir en línea con un nombre al poner la definición en parens para forzar la evaluación. Para funcs de ruptura muy simples u obvios, supongo que no es gran cosa, pero para mí es como semis. Realmente no me importa lo que haga el otro tipo si no causa dolor, pero siempre intento nombrar mis funcs porque no es difícil y puede ser una ventaja.
Creo que la única diferencia en un código como ese es que con el segundo fragmento de código puede volver a llamar a la misma función (a veces con "funciones de temporizador" es útil):
(function(){
function invokeMe() {
if(..) setTimeout(invokeMe, 5);
}
setTimeout(invokeMe, 5);
})();
En el ejemplo proporcionado, la declaración y el uso de la función son tan cercanos que creo que la única diferencia es la legibilidad. Prefiero el segundo ejemplo.
Hay una diferencia significativa entre los dos: el último tiene un nombre.
Me gusta ayudar a que mis herramientas me ayuden, así que evito las funciones anónimas, ya que mis herramientas no pueden darme información significativa sobre ellas (por ejemplo, en una lista de pila de llamadas en un depurador, etc.). Así que iría con el
(function(){
function invokeMe() {
/*code*/
}
setTimeout(invokeMe, 5);
})();
... forma en general. Las reglas están destinadas a ser rotas, sin embargo, no servilmente inclinadas a. :-)
Tenga en cuenta que según la especificación, hay una tercera alternativa: puede tener una función en línea que también tiene un nombre:
(function(){
setTimeout(function invokeMe(){ /*some code here*/ }, 5);
})();
El problema, sin embargo, es que cada versión hasta ahora del intérprete de JavaScript de Microsoft ("JScript"), que incluye (asombrosamente) la de IE9, maneja incorrectamente la expresión de función nombrada y crea dos funciones completamente distintas en momentos diferentes. ( Proof , pruébalo en IE9 o anterior y también en casi cualquier otro navegador). IE se equivoca de dos maneras: 1. Crea dos objetos de función separados, y 2. Como consecuencia de uno de esos, "sangra" "el símbolo del nombre en el alcance adjunto de la expresión (en clara violación de la Sección 13 de la especificación). Detalles aquí: doble toma
Las funciones nombradas predefinidas pueden reducir el problema de devolución de llamada de JavaScript, que se menciona en http://callbackhell.com/
No hay razones técnicas para preferir una versión sobre la otra. Para mí, generalmente depende de dos cosas:
- Quiero reutilizar la devolución de llamada pasada en otro contexto. En este caso, defino la función independiente y paso la referencia.
- La devolución de llamada es mayor que ~ 10 líneas de código y la función espera argumentos adicionales después de la devolución de llamada. En este caso, es difícil de reconstruir, qué valores se pasan realmente a la función.
Ejemplo:
setTimeout(function() { // I need to scroll to see the other arguments
// many lines of code
}, 0); // <- where does this ''0'' belong to?
OMI, declarar una función será útil solo si tiene la intención de volver a utilizarla más tarde, de alguna otra manera.
Personalmente uso expresiones de funciones (primera forma) para los manejadores de setTimeout
.
Sin embargo, es posible que desee conocer las diferencias entre las declaraciones de función y las expresiones de función, le recomiendo el siguiente artículo:
Prefiero usar funciones con nombre. Las funciones nombradas muestran por nombre en todos los depuradores (aire, firebug, IE).
Ejemplo:
Tenga en cuenta que también puede tener funciones nombradas en línea como
{
method: function obj_method(){}
}
De esta manera, cuando miras el rastro de la pila, verás la función obj_method en lugar de anonymous.
¿Estabas preguntando cuándo alinear una función en lugar de declararla? Cuando tiene sentido en el código. Si lo necesita de dos lugares diferentes, no puede estar en línea. Algunas veces, en línea, el código es más fácil de leer, a veces más difícil.
Sé que esta es una vieja pregunta, pero para mí hay una diferencia aún más importante que las ya mencionadas. izado Cada función tiene que crearse y, por lo tanto, se reserva un espacio en la memoria y, finalmente, tiene que ser GC más tarde.
Las funciones con nombre se elevan al principio de la función circundante y, por lo tanto, se crean en cada llamada a función, independientemente de si se usan o no. Las funciones anónimas se crean solo si se ejecuta el código que las define.
//an example where you wold prefer to use an anonymous function.
//you can assign this (anonymous) function to a variable, so you get your "name" back.
function someFn(){
if(condition){
//the variable declaration has been hoisted,
//but the function is created at this point, and only if necessary.
var process = function(value){/* */};
switch(condition2){
case 1: process(valueFor1); break;
case 2: process(valueFor2); break;
/* ... */
}
}
}
function someFn(){
var process;
if(condition){
process = function(value){ /* A */ }
}else{
process = function(value){ /* B */ }
}
//beware, depending on your code, "process" may be undefined or not a function
process(someValue);
}
//an example where you would prefer (/ utilize) the hoisting.
function someFn(){
/* some code */
while(condition){
//some might want to keep the function definition near the code where it is used,
//but unlike an anonymous function or a lambda-expression this process-function
//is created only once per function-call, not once per iteration.
function process(value, index){ /* ... */ }
/* ... */
process(value, index)
}
}
Entonces, como regla general:
dentro de un bucle no debería haber función anónima o lambda-expresión
si necesita la función solo dentro de una condición (raramente verdadera), debería preferir las funciones anónimas a las nombradas, ya que solo se crean cuando es necesario.
si conoce su negocio (JavaScript), sabe cuándo ignorar este consejo
Sugiero un duelo completo entre los miembros del equipo oponente para resolver esos argumentos.
Más en serio, al final simplemente no importa. La primera forma (funciones sin nombre) tiende a ser difícil de manejar con funciones más grandes, pero no es un gran problema en absoluto con funciones pequeñas (1-2 líneas). La segunda forma es similarmente inofensiva.
Cualquier argumento en contra de cualquier estilo es puro bikeshedding , imo.
Tiendo hacia funciones nombradas también. Los refs de función anónimos son rápidos, pero solo deberían usarse para cosas simples. Mi regla de oro es que si la función es más de 2 líneas de código, probablemente pertenece a su propia definición.
Esto es complicado por la mayoría de los códigos de ejemplo que hacen uso de las funciones anónimas. Pero las muestras suelen ser muy simplistas. El método se desmorona a medida que las cosas se complican. He visto funciones refs anidadas en refs de funciones ya que el desarrollador se dio cuenta de que se necesitaban más devoluciones de llamada en pasos posteriores. En lugar de esta lógica basada en árbol, prefiero la organización de funciones aisladas.
Y generalmente me siento feliz de poder reutilizar una de las funciones que defino más adelante.
Un uso importante de una función anónima es cuando necesita pasar datos de ámbito a su llamada de función, pero normalmente solo envuelvo mi función en la función anónima.
Las funciones nombradas también son absolutamente necesarias si alguna vez ingresas en el desarrollo impulsado por prueba.
Una función en línea evita la contaminación del espacio de nombres y las funciones predefinidas tienen una mayor reutilización. Creo que podrías hacer casos donde cada uno sea apropiado.
Funciones nombradas
Existe un serio uso incorrecto de la terminología en la pregunta y las respuestas en esta página. No hay nada sobre si una función está o no en línea (una expresión de función) que dice que no puede nombrarla.
Esto es usando una expresión de función :
setTimeout(function doSomethingLater() { alert(''In a named function.''); }, 5);
y esto es usando una declaración de función :
function doSomethingLater() { alert(''In a named function.''); }
setTimeout(doSomethingLater, 5);
Ambos ejemplos usan funciones con nombre y ambos obtienen los mismos beneficios cuando se trata de depurar y perfilar herramientas.
Si se especifica el nombre (el texto después de "función" pero antes del paréntesis), entonces es una función con nombre, independientemente de si está en línea o se declara por separado. Si el nombre no está especificado, entonces es "anónimo".
Nota: TJ señala que IE maneja mal las expresiones de funciones nombradas de una manera no trivial (Ver: http://kangax.github.com/nfe/#jscript-bugs ) y esto es importante tener en cuenta, simplemente estoy tratando de hacer un punto acerca de la terminología.
¿Cuál deberías usar?
En respuesta a su pregunta directa, debe usar una declaración de función con nombre si la función alguna vez podría usarse desde cualquier otro lugar en su código. Si la función se está utilizando en exactamente un lugar y no tiene relevancia en ningún otro lugar, entonces utilizaría una expresión de función a menos que sea prohibitivamente larga o que se sienta fuera de lugar (por razones de estilo). Si usa una expresión de función en línea, a menudo es útil nombrarla de todos modos para depuración o claridad del código.
Pérdidas de memoria
Ya sea que nombre su función, use una declaración de función o use una expresión de función tiene poco impacto en el problema de pérdida de memoria. Déjame intentar explicar qué causa estas filtraciones. Eche un vistazo a este código:
(function outerFunction() {
var A = ''some variable'';
doStuff();
})();
En el código anterior, cuando "outerFunction" termina "A" sale del alcance y se puede recolectar basura, liberando esa memoria.
¿Qué pasa si agregamos una función allí?
(function outerFunction() {
var A = ''some variable'';
setTimeout(function(){ alert(''I have access to A whether I use it or not''); }, 5);
})();
En este código (arriba) la expresión de función que estamos pasando a setTimeout tiene una referencia a "A" (a través de la magia de cierre) e incluso después de que "outerFunction" termina "A" permanecerá en la memoria hasta que se active el tiempo de espera y la función es desreferenciado
¿Qué pasa si pasamos esa función a algo que no sea setTimeout?
(function outerFunction() {
var A = ''some variable'';
doStuff(function(){ alert(''I have access to A whether I use it or not''); });
})();
function doStuff(fn) {
someElement.onclick = fn;
}
Ahora la expresión de función que estamos pasando a "doStuff" tiene acceso a "A" e incluso después de que "outerFunction" termine "A" permanecerá en la memoria mientras haya una referencia a la función que pasamos a doStuff . En este caso, estamos creando una referencia a esa función (como un controlador de eventos) y, por lo tanto, "A" permanecerá en la memoria hasta que se borre ese controlador de eventos. (por ejemplo, alguien llama a someElement.onclick = null
)
Ahora mira lo que sucede cuando usamos una declaración de función:
(function outerFunction() {
var A = ''some variable'';
function myFunction() { alert(''I have also have access to A''); };
doStuff(myFunction);
})();
¡El mismo problema! "myFunction" se limpiará solo si "doStuff" no contiene una referencia y "A" solo se limpiará cuando se limpia "myFunction". No importa si usamos una declaración o una expresión; lo que importa es si se crea una referencia a esa función en "doStuff"!