Estoy viendo la práctica de ES2015 recientemente, hay un dicho que dice
No hay alcance a nivel de bloque en JavaScript
Es posible que no comprenda este problema, echemos un vistazo a un ejemplo primero
var a = [] for (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();Creo que muchas personas piensan que el resultado de esta pregunta es 6, pero desafortunadamente, la respuesta es 10. Intenta algo más. A [7] (), A [8] (), y A [8] () tienen 10 !!
Dado que JS a menudo envuelve las variables primitivas en los objetos correspondientes cuando se procesan, por ejemplo, para var str = "Hello World"; str.slice (1).
El proceso real de js es probablemente var str = "hello world"; nueva cadena (str) .slice (1). Tal proceso puede causar problemas para que comprendamos el problema.
Para explicar este problema aquí, pertenezco al tipo de número en el tipo primitivo, lo declaro explícitamente como el tipo de número. Dado que el proceso de asignación del tipo básico es volver a aplicar la memoria y modificar la dirección de la variable, también utilizamos el proceso de objeto de número nuevo para simular este proceso. El código modificado es el siguiente:
var a = [] var i = nuevo número (0); for (; i <10; i = nuevo número (i+1)) {a [i] = function () {console.log (i.ToString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10Echemos un vistazo a las direcciones de memoria relativa de estas variables en combinación con un programa.
(function () {var id = 0; function generateId () {return id ++;}; object.prototype.id = function () {var newId = generateId (); this.id = function () {return newID;}; return newD;};}) (); var a = [] var i = new number (0); console.log (i.id (); nuevo número (i+1), i.id ()) {a [i] = function () {console.log (i.id ()); console.log (I.ToString ()); }} a [6] (); // 10 10a [7] (); // 10 10a [8] (); // 10 10a [9] (); // 10 10console.log (i.id ()) // 10Aquí hemos simulado todo el efecto de "asignación" de nuestra I, y la dirección relativa de I cambió de 0 a 10 (al final, debe agregarse una vez antes de que pueda saltar del bucle for).
Al observar la dirección relativa de I, encontramos un problema: cuando la función correspondiente a un [x] (x: 0 ~ 9) se ejecuta, la dirección relativa de I referenciada es 10. ¿Por qué?
Aquí involucraremos el problema de alcance de nivel de bloque. Aquí citamos un pasaje de Ruan Yifeng en la introducción a ES6:
ES5 solo tiene alcance global y alcance de funciones, pero no hay alcance de nivel de bloque.
ES5 es la versión más utilizada de JS. Esta oración dice que en JavaScript, no hay alcance de bloque. Solo hay alcance global y alcance a nivel de bloque.
¿Cómo entender? Por ejemplo
for (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (window.i); // 10Intuitivamente, creemos que el bucle for es un bloque de código y debe pertenecer a un alcance de nivel de bloque. Sin embargo, no solo puede emitir 0 a 9 normalmente, sino que también puede emitir 10 externamente en el bucle for. Al mismo tiempo, descubrimos que aunque definimos i en el bucle for, parece que estoy colgado en el objeto de ventana global (si es el entorno de ejecución de NodeJs, estará colgado del objeto global)
Por lo tanto, los bloques como para los bucles en JavaScript no tendrán el efecto de un alcance de nivel de bloque. La definición de variables en bloques de código como para bucles no es diferente de definir directamente las variables en el alcance actual.
Pero podemos aislar el alcance a través de funciones:
(function () {for (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); //// no está definidoAl mismo tiempo, si console.log (window.i); se ejecuta, se obtendrá el resultado indefinido. Aquí usamos una función de ejecución inmediata para formar un alcance. Desempeña un papel similar a un bloque de código. Después de este alcance de la función, la variable ya no se puede acceder. Sin embargo, se puede acceder en cualquier momento dentro del alcance de la función.
Regrese a la pregunta anterior, y luego lo entenderemos en combinación con solo el alcance global y el alcance de nivel de bloque en JavaScript. En el bucle for, el i definido debe definirse en el alcance actual, es decir, el alcance de la ventana. En el cuerpo del bucle, asignamos una función a un [i]. Cuando ejecutamos esta función, la situación es la siguiente:
No existe en la función, por lo que seguimos la cadena de alcance para encontrar i. El que salimos en este momento es este i. Dado que salto del último +1 del bucle, me convierte en 10, por lo que el resultado de la salida siempre es 10. Pero lo que realmente necesitamos no es el último I, sino en el proceso intermedio. Si queremos resolver este problema, necesitamos dejar de lado la variable I (porque lo último que inevitablemente se convierte en 10). Necesitamos dejar que la función correspondiente a un [0] se refiera al valor 0 y dejar que la función correspondiente a un [1] se refiera al valor 1. Como se muestra en la figura a continuación:
Vuelve a nuestro código anterior.
La flecha de la figura muestra que podemos acceder a I (0 ~ 9). Aquí, debido a que el bucle for no forma un alcance de nivel de bloque por sí mismo, accedemos a I definido por el bucle for cuando seguimos la cadena de alcance.
Aquí envolvemos nuestro código con una función de ejecución inmediata, que puede formar un alcance, y pasamos el valor I para ello. La siguiente:
var a = [] var i = nuevo número (0); console.log (i.id ()); // 0for (; i <10; i = nuevo número (i+1), i.id ()) {(function (i) {a [i] = function () {console.log (i.id ()); console.log (i.ToString ();}}) (i); a [6] (6]; // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9console.log (i.id ()); // 10}Dado que esta función de ejecución inmediata se refiere al valor numérico 0 ~ 9, cuando ejecutamos la función A [i], primero encontraremos el alcance de la función de ejecución inmediata a lo largo de la cadena de alcance. La función de ejecución inmediata mantiene la referencia numérica desde 0 ~ 9, y podemos generar correctamente el valor de I en la función A [i]. A través del resultado de la ejecución, podemos ver que no solo el resultado de la ejecución es correcta, sino que la dirección de memoria relativa del valor al que hacemos referencia también es correcta. Luego cambiamos el objeto número que originalmente se declaró explícitamente para las pruebas. Como sigue:
var a = []; for (var i = 0; i <10; i ++) {(function (i) {a [i] = functer () {console.log (i);}}) (i);}Finalmente, echemos un vistazo a la sintaxis ES6 recomendada usando LET en lugar de VAR y compilando y generando código ES5 a través de Baable:
// ES6 Código var a = [] para (let i = 0; i <10; i ++) {a [i] = functer () {console.log (i); }} a [6] (); // Babel compiló y generó el código ES5 "Use estricto"; var a = []; var _loop = function _loop (i) {a [i] = function () {console.log (i); };}; para (var i = 0; i <10; i ++) {_loop (i);} a [6] ();Veamos si nuestra solución es muy similar a la solución ES6. Aquí, nuestra función de ejecución inmediata es equivalente a la ejecución de la función _loop y _loop (i) en el código ES5 generado.