Un cierre es una función y el entorno del ámbito de esa función.
Ayuda a comprender cómo Javascript implementa el alcance en este caso. De hecho, es solo una serie de diccionarios anidados. Considere este código:
var global1 = "foo";
function myFunc() {
var x = 0;
global1 = "bar";
}
myFunc();
Cuando el programa se pone en marcha, tiene un diccionario ámbito individual, el diccionario mundial, lo que podría tener un número de cosas definidas en ella:
{ global1: "foo", myFunc:<function code> }
Diga usted llama myFunc , que tiene una variable local x. Se crea un nuevo alcance para la ejecución de esta función. El ámbito local de la función se ve así:
{ x: 0 }
También contiene una referencia a su ámbito primario. Por lo tanto, todo el alcance de la función es el siguiente:
{ x: 0, parentScope: { global1: "foo", myFunc:<function code> } }
Esto permite que myFunc modifique global1. En Javascript, siempre que intenta asignar un valor a una variable, primero verifica el alcance local para el nombre de la variable. Si no se encuentra, comprueba el parentScope y el parentScope del alcance, etc. hasta que se encuentre la variable.
Un cierre es literalmente una función más un puntero al alcance de esa función (que contiene un puntero a su alcance principal, y así sucesivamente). Así, en su ejemplo, después del bucle for
ha finalizado la ejecución, el alcance podría tener este aspecto:
setupHelpScope = {
helpText:<...>,
i: 3,
item: {'id': 'age', 'help': 'Your age (you must be over 16)'},
parentScope: <...>
}
Cada cierre se crea apuntará a este objeto alcance individual. Si tuviéramos que enumerar cada cierre que ha creado, se vería algo como esto:
[anonymousFunction1, setupHelpScope]
[anonymousFunction2, setupHelpScope]
[anonymousFunction3, setupHelpScope]
Cuando cualquiera de estas funciones se ejecuta, utiliza el objeto alcance que se aprobó - en este caso, es el mismo alcance objeto para cada función! Cada uno mirará la misma variable item
y verá el mismo valor, que es el último establecido por su bucle for
.
Para responder a su pregunta, no importa si agrega var item
sobre el lazo for
o dentro de él. Como los bucles for
no crean su propio ámbito, item
se almacenará en el diccionario de alcance de la función actual, que es setupHelpScope
. Los gabinetes generados dentro del bucle for
siempre apuntarán al setupHelpScope
.
Algunas notas importantes:
- Este comportamiento se produce porque, en Javascript,
for
bucles no tienen su propio ámbito - sólo tiene que utilizar el alcance de la función de cerramiento. Esto también es cierto para if
, while
, switch
, etc. Si esto fuera C#, por otro lado, se crearía un nuevo objeto de ámbito para cada ciclo, y cada cierre contendría un puntero a su propio alcance único.
- Observe que si
anonymousFunction1
modifica una variable en su ámbito, modifica esa variable para las demás funciones anónimas. Esto puede conducir a algunas interacciones realmente extrañas.
- Los ámbitos son solo objetos, como los que usted programa. Específicamente, son diccionarios. La máquina virtual JS gestiona su eliminación de la memoria como cualquier otra cosa, con el recolector de basura. Por esta razón, el uso excesivo de cierres puede crear una hinchazón de memoria real. Dado que un cierre contiene un puntero a un objeto de ámbito (que a su vez contiene un puntero a su objeto de ámbito principal y así sucesivamente), no se puede recolectar la cadena completa del alcance y debe permanecer en la memoria.
Más información:
javascript has 'let'? mirando hacia arriba ... – Kobi
@Kobi: Es una extensión específica de Mozilla para JavaScript. Ver [esto] (https://developer.mozilla.org/en/new_in_javascript_1.7). –
Si es específico de Mozilla, ¿significa eso que debo evitar usarlo? – Nick