¿Por qué no se anima a crear funciones dentro de un bucle en JavaScript?

Soy un novato absoluto, y acabo de leer esto en JavaScript: The Good Parts .

En el capítulo sobre el scope, dice: " Es importante comprender que la function interna tiene acceso a las variables reales de las funciones externas y no a las copys para evitar el siguiente problema ". Y luego los dos siguientes ejemplos se ven así:

//MAL EJEMPLO

var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); }; } }; 

// END MALO EJEMPLO

 var add_the_handlers = function (nodes) { var helper = function (i) { return function (e) { alert(i); }; }; var i; for (i = 0; i < nodes.length; i += 1) { modes[i].onclick = helper(i); } }; 

Según el autor, el segundo ejemplo es mejor porque no utiliza un bucle dentro de la function, de lo contrario podría ser un desperdicio computacional. Pero estoy perdido y no sé qué hacer con ellos. ¿Cómo pongo su teoría en una aplicación real? ¿Puede alguien ilustrar estos dos ejemplos combinar HTML?

El problema es con el cierre. Las funciones internas tienen acceso a la variable que i fuera de estas funciones. Después de que se hayan ejecutado todas las iteraciones del ciclo, la variable i tendrá el valor de nodes.length . Entonces, cuando haces clic en los nodes[0] , la alerta dirá nodes.length , que no es lo que esperas. (Esperaría que la alerta dijera 0 ). Lo mismo ocurre cuando hace clic en nodes[1] , nodes[2] , etc. La alerta para todos ellos indicará nodes.length .

Puede verificarlo con HTML trabajando aquí: https://jsfiddle.net/vdrr4519/ .

Los elementos 'multifunc' se insertan con el ejemplo con muchas funciones, 'singlefunc', con una sola. Mira, tomamos todos los elementos con una class y los pasamos a la function.

 multifunc(document.querySelectorAll('.multifunc')); 

La function ejecuta el bucle 'for' y agrega 'click' al detector de events. Por lo tanto, el elemento debe alertar a su índice onclick (comenzando desde 0). Pero en el ejemplo con muchas funciones, se produce un valor incorrecto (debido al cierre, otras respuestas también resaltan el problema).

Creo que debería decir también que no se trata de una function única / funciones múltiples: se trata de trabajar con cierres. Verá, puedo implementar un ejemplo de trabajo CON muchos cierres: https://jsfiddle.net/pr7gqtdr/1/ . Básicamente hago lo mismo que en un manejador de function única, pero cada vez que llamo a la nueva function 'ayudante':

  nodes[i].onclick = function (i) { return function (e) { alert(i); }; }(i); 

Ver, esto (i) al final es una llamada de function inmediata, por lo que onclick obtiene una function con la variable i establecida en el cierre.

Pero, las opciones de function única es un poco mejor, porque es más eficiente con la memory, supongo. Las funciones son objects. Si crea muchos de ellos, toma más memory, en general. Entonces, eligiendo de estos, me quedaría con la opción de la function 'controller'.

El mal ejemplo crea una gran cantidad de controlleres de events; Uno por evento El buen ejemplo crea un único controller de events y lo asigna a todos los events.

Con el mal ejemplo, ha creado muchas funciones separadas, en lugar de solo una. Eso puede ser una gran cantidad de gastos generales adicionales y una gran cantidad de posibles problemas de scope. Estos incluyen problemas de cierre, como un evento que solo se activa para el último elemento del ciclo.

Además, el buen ejemplo le permite cancelar más fácilmente los events porque tiene acceso al puntero de function original.

El buen ejemplo también es más fácil de leer y entender. El ciclo solo se usa para crear los elementos y enlazar sus events; El event handling esos events se realiza en otro lugar.

Como menciona Soviut, está creando muchos controlleres de events en el mal ejemplo. Además, es importante señalar que las funciones de ejemplo incorrecto se refieren a la misma variable i , lo que significa que todas ellas tendrán el último valor de nodes.length cuando se ejecuten.

Esto es porque se crea un cierre. Puede leer más sobre esto en Closures .

En primer lugar, en el mal ejemplo, se crea una function para cada controller de events; el bucle crea múltiples objects de function. Mientras que en el segundo ejemplo, se crea una sola function y se hace reference desde el interior del bucle. Así que ahorras mucha memory.

En segundo lugar, en el mal ejemplo, cuando se ejecuta el valor de "i", la function no retiene el valor, y cuando se ejecuta, siempre devuelve el último valor de "i". Sin embargo, en el buen ejemplo, cuando se pasa "i" a la function, este valor se conserva como el entorno léxico de la function, y cuando se llama, devolverá el valor correcto.

En tercer lugar, como lo menciona @Gary Hayes, es posible que deseemos utilizar la function en cualquier otro lugar también. Por lo tanto, es mejor mantenerlo independiente del ciclo.