En el proceso de aprendizaje front-end, inevitablemente encontraremos muchos problemas, por lo que hoy hablaremos de dos preguntas desde la perspectiva de un principiante:
¿Qué es un cierre?
¿Cuáles son las funciones de los cierres?
De hecho, los cierres están en todas partes cuando aprendemos JavaScript, solo necesitas poder reconocerlos y aceptarlos. Los cierres no son una herramienta que requiera aprender una nueva sintaxis o patrón para usar. Los cierres son una consecuencia natural de escribir código basado en el alcance léxico. Rara vez necesitamos crear cierres intencionalmente al escribir código.
Creo que muchos amigos ya están murmurando en sus corazones en este momento, ¿cuál es este alcance léxico? No entres en pánico, escúchame despacio, el alcance léxico es el alcance definido en la etapa léxica. En otras palabras, el alcance léxico está determinado por dónde coloca las variables y los alcances a nivel de bloque cuando escribe su código, por lo que el alcance permanece sin cambios cuando el analizador léxico procesa el código (la mayor parte del tiempo). ——"JavaScript que no conoces"
Primero tomemos un ejemplo:
función prueba(){ vararr = [] para(var i=0;i<10;i++){ arreglo[i]=función(){ consola.log(i); } } regreso } var miArr = prueba() // miArr[0]() // miArr[1]() //... para(var j = 0; j < 10; j++){ miArr[j]() } // Para evitar el tedio, aquí se usa un segundo bucle para llamar a la función en el primer bucle en la función de prueba e imprimir diez resultados.
Primero analicemos este código: cuando se ejecuta este código, de acuerdo con el sentido común. debe analizarse Imprime diez números del 0 al 9 en secuencia, pero el bucle for no tarda en ejecutarse (descuidado en microsegundos). Cuando la función test devuelve arr, hay 10 function(){console. );}, la función en la matriz no se ejecuta en este momento. Cuando var myArr = test() llama a la función de prueba, dado que se ignora el tiempo de ejecución del bucle for, i ya es 10 en este momento, entonces, ¿qué es? impreso es 10 sobre 10.
Creo que alguien preguntará en este momento, ¿qué tiene esto que ver con el cierre del que vamos a hablar? Entonces, si modificamos ligeramente este código y lo convertimos en un acumulador, ¿cómo podemos implementarlo?
Creo que habrá peces gordos en este momento que dirán: ¿no es así de simple?
Cambie la definición de var a una definición de let para que el primer bucle for se convierta en un alcance a nivel de bloque y luego pueda convertirse en un acumulador. Por supuesto que no hay problema,
pero de lo que hablamos hoy es de cómo implementar un acumulador en ES5. Entonces echemos un vistazo al siguiente código:
función prueba(){ vararr = [] para(var i=0;i<10;i++){ (función(j){ arr[j]=función(){ consola.log(j); } })(i) } regreso } var miArr = prueba() para(var j = 0; j < 10; j++){ miArr[j]() }
Los amigos cuidadosos definitivamente encontrarán que esto es cambiar el cuerpo de la función en el bucle a una función autoejecutable, pero el resultado de salida en este momento es generar diez números del 0 al 9 en secuencia, y esto incluye el cierre Paquete, Cuando comenzamos a ejecutar este código, se llamará al segundo bucle for diez veces. Cuando se ejecute cada función autoejecutable, se creará un objeto AO de la función autoejecutable. El nombre del atributo es j. Normalmente, después de ejecutar la función de ejecución, su objeto AO debe destruirse. Sin embargo, cuando se ejecuta myarr[j] (), el objeto AO de arr[j] está en la parte superior de la cadena de alcance. Ahora busqué el nombre del atributo j, pero no lo encontré. Busqué en la cadena de alcance y lo encontré en el objeto AO de la función autoejecutable. Por lo tanto, cuando finaliza la función autoejecutable, es AO. El objeto no será reciclado por el mecanismo de recolección de basura; de lo contrario, se informará un error cuando se ejecute myarr[j] () y se formará un cierre.
Tomemos otra
función de ejemplo a(){. función b(){ var bbb = 234 consola.log(aaa); } var aa = 123 return b // b nació en a, pero fue salvo} varglob = 100 var demostración = a()Primero usamos la precompilación para analizar el código de
demo()
. Primero, definimos un objeto GO global y buscamos la declaración de variable global que se utilizará como el nombre del atributo de GO. no está definido en la declaración global. Para la declaración de función, el nombre de la función se utiliza como el nombre del atributo del objeto GO y el valor se asigna al cuerpo de la función. En este momento debería ser GO{ glob: undefinido--->100; demo: undefinido; a: fa(){} }; Luego crea un AO{ aaa: undefinido--->123;b: fb() para función a {} }, y finalmente precompilar la función b en la función a para crear un AO de b { b: indefinido ---> 234} en este momento, el orden de la cadena de alcance es 1. Objeto AO de la función b; 2. Objeto AO de la función a; 3. Objeto GO global. Cuando imprimimos aaa en la función b, comenzamos desde la parte superior de la cadena de alcance. Si no hay aaa en el objeto AO de la función b, buscaremos hacia abajo a lo largo de la cadena de alcance para encontrar el AO de la función de segundo nivel a. El objetivo es encontrar el valor de aaa como 123 y generar el resultado.
Si no lo analizamos desde la perspectiva de la precompilación, pensaríamos que aaa debería informar un error en este momento. Cuando se ejecuta var demo = a(), cuando finaliza la ejecución de la función a, se genera el objeto AO correspondiente. a debe destruirse según el análisis de sentido común: cuando ejecutamos la demostración, la cadena de alcance debe crear el objeto AO y el objeto GO de b. En este momento, solo existe el objeto AO de b y ningún objeto AO de a. El valor de aaa no debe imprimirse, pero en este momento, el valor de aaa El valor es 123, lo que significa que el objeto AO de a no ha sido destruido, entonces, ¿por qué? La razón es que aquí se crea un cierre. Cuando se completa la ejecución de var demo = a(), el mecanismo de recolección de basura le preguntará: Hermano, una función, creo que ha terminado de ejecutarla. ¿Puede liberarme su memoria de ejecución? ?, pero en este momento la función a solo podía sacudir la cabeza con impotencia y decir: Hermano, no estoy seguro de haber completado la ejecución. Después de ejecutarla, creé a b, pero b no está bajo mi control, así que lo estoy. No estoy seguro de si se llamó a b, así que no estoy seguro de haber completado la ejecución. El mecanismo de recolección de basura lo pensó. Como no lo sabes, no lo reciclaré. Debo informar un error, por lo que en este momento el objeto AO no se recicla.
Creo que a través de estos dos ejemplos, ya tienes una comprensión general de los cierres. A continuación, hablemos de las funciones de los cierres.
La funciónde
cierre es
- se puede
- implementar variables públicas. Por ejemplo: el acumulador (3.js)
- almacenar en caché
- para lograr encapsulación, privatización
- y desarrollo modular de atributos para evitar la contaminación de variables globales.
3.js).
recuento de variables = 0 función agregar() { recuento de retornos++ } consola.log(add()); consola.log(add()); console.log(add());
Este es un código de acumulación relativamente común, pero si durante nuestra pasantía o incluso en el trabajo, la empresa requiere que encapsule el acumulador en un código modular, entonces, en este momento, por el bien de módulo Intentamos evitar definir variables globales tanto como sea posible, pero ¿cómo podemos lograrlo sin definir variables globales? En este momento podemos usar cierres;
función agregar() { recuento de variables = 0 función a() { ++ contar consola.log(recuento); } devolver un } var res = agregar() res() res() // Una vez finalizada la función agregar, el objeto AO de agregar no se destruye, porque después de ejecutar la función agregar, el a devuelto forma un cierre sin saber si se ha llamado, por lo que se puede encapsular en un módulo sin usar Acumulador global de variables.
Estas son algunas de mis opiniones personales sobre los cierres y sus funciones. En la actualidad, solo tengo una comprensión superficial de los cierres. Después de estudiar y mejorar, habrá artículos posteriores sobre los cierres. Gracias por mirar. corregir y progresar juntos.