Reponer:
El cierre es una dificultad en el lenguaje JavaScript y su característica. Muchas aplicaciones avanzadas dependen de los cierres para implementar.
Características de cierre
Los cierres tienen tres características:
1. Funciones Funciones anidadas
2. La función puede referirse a parámetros y variables externas dentro
3. Los parámetros y variables no serán recolectados por el mecanismo de recolección de basura
Definición de cierre y sus ventajas y desventajas
Los cierres se refieren a funciones que tienen acceso a variables en el alcance de otra función. La forma más común de crear cierres es crear otra función dentro de una función y acceder a las variables locales de esta función a través de otra función.
La desventaja de los cierres es que son la memoria de los residentes, lo que aumentará el uso de la memoria, y el uso inadecuado puede conducir fácilmente a la fuga de memoria.
Los cierres son una característica importante del lenguaje JavaScript. La aplicación principal de los cierres es principalmente para: diseñar métodos y variables privadas.
Después de ejecutar la función general, el objeto activo local se destruye y solo el alcance global se guarda en la memoria. ¡Pero la situación de cierre es diferente!
Hablar sobre el tema
1. Definición de cierre?
Echemos un vistazo a algunas definiciones sobre los cierres:
1. El cierre se refiere a una función que tiene permiso para acceder a variables en otro alcance de la función.
2. Los objetos de función se pueden asociar a través de cadenas de alcance, y las variables dentro del cuerpo de la función se pueden guardar en el alcance de la función. Esta característica se llama 'cierre'.
3. Las funciones internas pueden acceder a parámetros y variables de funciones externas que los definen (excepto esto y los argumentos).
Si desea aprender sistemáticamente el concepto de cierre JS, puede consultar la columna JS E-Book del sitio web de Wulin.com para aprender.
Resumamos la definición
1. Puede acceder a funciones de variables en el alcance de la función externa
2. Las variables de las funciones externas a las que se accede por funciones internas se pueden guardar dentro del alcance de la función externa sin reciclar, este es el núcleo. Más tarde, cuando encontramos cierres, debemos pensar en ello. Debemos centrarnos en la variable a la referencia por el cierre.
Crear un cierre simple
var saysname = function () {var name = 'jlozo'; return function () {alert (name);}}; var dice = sayname (); decir();Interpretemos las siguientes dos oraciones:
• var dice = sayname (): devuelve una función interna anónima almacenada en la variable Say y se refiere al nombre de la variable de la función externa. Debido al mecanismo de recolección de basura, después de que se ejecuta la función SayName, el nombre de la variable no se destruye.
• Say (): ejecute la función interna devuelta y aún acceda al nombre de la variable y la salida 'JOZO'.
2. Cadena de alcance en cierre
Comprender las cadenas de alcance también es útil para comprender los cierres.
Los métodos de búsqueda de variables en el alcance deben ser muy familiares, pero de hecho, esto es lo que está buscando hacia arriba a lo largo de la cadena de alcance.
Cuando se llama la función:
1. Primero cree un contexto de ejecución y la cadena de alcance correspondiente;
2. Agregue argumentos y otros valores de parámetros nombrados al objeto activo de la función (objeto de activación)
Cadena de alcance: el objeto activo de la función actual tiene la prioridad más alta, seguido por el objeto activo de la función externa, y el objeto activo de la función externa de la función externa disminuye en secuencia hasta el final de la cadena de alcance: el alcance global. La prioridad es el orden de las búsquedas variables;
Primero veamos una cadena de alcance normal:
function saysname (name) {return name;} var dice = sayname ('jzo');Este código contiene dos ámbitos: a. alcance global; B. Sayname Función Alcance, es decir, solo hay dos objetos variables. Cuando se ejecuta al entorno de ejecución correspondiente, el objeto variable se convertirá en un objeto activo y se empujará al extremo frontal de la cadena de alcance del entorno de ejecución, es decir, se convierte en la prioridad más alta. Habla con la imagen:
Esta imagen también está disponible en JS Advanced Programming Books, y lo he vuelto a dibujar todo.
Al crear la función sayname (), se crea una cadena de alcance que contiene el objeto variable de antemano, es decir, la cadena de alcance indexada por 1 en la figura, y se guarda en el atributo interno [[alcance]]. Cuando se llama a la función sayname (), se crea un entorno de ejecución y luego la cadena de alcance se construye copiando el objeto en el atributo [[alcance]] de la función. Después de eso, se crea otro objeto activo (indexado por 0 en la figura) y se empuja en el extremo frontal de la cadena de alcance del entorno de ejecución.
En términos generales, cuando se ejecuta la función, el objeto activo local se destruirá y solo el alcance global se guarda en la memoria. Sin embargo, la situación de los cierres es diferente:
Echemos un vistazo a la cadena de alcance de los cierres:
function saysname (name) {return function () {return name;}} var dice = sayname ('Jozo');Esta instancia de cierre tiene un alcance más para la función anónima que el ejemplo anterior:
Después de que se devuelve la función anónima de la función sayname (), su cadena de alcance se inicializa a un objeto activo y un objeto variable global que contiene la función sayname (). De esta manera, la función anónima puede acceder a todas las variables y parámetros definidos en SayName (). Más importante aún, después de la ejecución de la función SayName (), su objeto activo no se destruirá, porque la cadena de alcance de la función anónima aún se refiere al objeto activo. En otras palabras, después de la ejecución de la función SayName (), la cadena de alcance de su entorno de ejecución será destruida, pero su objeto activo se dejará en la memoria, sabiendo que la función anónima será destruida. Este es también el problema de fuga de memoria que se discutirá más adelante.
No escribo tanto sobre problemas de la cadena de alcance, y escribir cosas en el libro también es muy agotador o (□) o
3. Ejemplo de cierre
Ejemplo 1: Implementación de la acumulación
// Método 1var a = 0; var add = function () {a ++; console.log (a)} add (); add (); // Método 2: cierre var add = (function () {var a = 0; return function () {a ++; console.log (a);}}) (); console.log (a); // UndefineDAdd (); add ();En comparación, el método 2 es más elegante y también reduce las variables globales y privatiza las variables.
Ejemplo 2: Agregar evento Haga clic a cada Li
var oli = document.getElementsByTagName ('li'); var i; for (i = 0; i <5; i ++) {oli [i] .onclick = function () {alert (i);}} console.log (i); // 5 // ejecutar function anonymous (function () {alert (i); // 5} ());Lo anterior es un ejemplo clásico. Todos sabemos que el resultado de la ejecución es que aparece 5, y también sabemos que los cierres se pueden usar para resolver este problema, pero al principio, todavía no puedo entender por qué aparece 5 y por qué los cierres pueden resolver este problema. Más tarde, lo resolví y lo dejé en claro:
a. Primero analicemos la situación antes de que se use el cierre: en el bucle for, unimos una función anónima a cada evento de clic Li, y el valor de la variable i devuelve en la función anónima. Cuando termina el bucle, el valor de la variable I se convierte en 5. En este momento, hacemos clic en cada li, es decir, ejecutar la función anónima correspondiente (consulte el código anterior). Esta es la variable I ya es 5, por lo que cada clic aumenta 5. Debido a que cada función anónima devuelta aquí se refiere a la misma variable I, si creamos una nueva variable para guardar el valor I actual de I cuando se ejecuta el bucle, luego deje que la función anónima aplique esta variable y finalmente devuelva esta función anónima, para que nuestro propósito pueda alcanzar. ¡Esto se logra usando cierres!
b. Analicemos la situación al usar cierres:
var oli = document.getElementsBytagName ('li'); var i; for (i = 0; i <5; i ++) {oli [i] .onclick = (function (num) {var a = num; // para ilustrar el problema de retorno function () {alerta (a);}}) (i)} console.log (i); // 5Cuando se ejecuta For Loop, la función anónima vinculada al evento de clic se pasa I y se ejecuta inmediatamente para devolver una función anónima interna. Debido a que los parámetros se pasan por valor, el número de parámetro formal guarda el valor actual de I, y luego asigna el valor a la variable local a. Luego, la función anónima interna mantiene la referencia de A, es decir, mantiene el valor actual de i. Entonces, después de ejecutar el bucle, haga clic en cada Li, y la función anónima devuelta aparecerá el valor del a.
4. Aplicación de cierres
Echemos un vistazo al propósito de los cierres. De hecho, al usar cierres, podemos hacer muchas cosas. Por ejemplo, simule el estilo de código orientado a objetos; expresar código de manera más elegante y concisa; y mejorar la eficiencia de la ejecución del código en algunos aspectos.
1. Función anónima de auto-ejecución
En situaciones reales, a menudo encontramos una situación en la que algunas funciones solo necesitan ejecutarse una vez y sus variables internas no necesitan mantenerse, como la inicialización de la UI, para que podamos usar los cierres:
// Cambie todas las fuentes de Li a rojo (function () {var els = document.getElementsBytagName ('li'); for (var i = 0, lng = els.length; i <lng; i ++) {els [i] .style.color = 'rojo';}}) ();Creamos una función anónima y la ejecutamos de inmediato. Dado que el externo no puede referirse a las variables dentro de él, variables locales como ELS, I y GNL se lanzarán poco después de la ejecución, ¡guardando la memoria!
La clave es que este mecanismo no contaminará el objeto global.
2. Implementar la encapsulación/código modular
var persona = function () {// El alcance de la variable está dentro de la función, y no se puede acceder al nombre var = "predeterminado" afuera. return {getName: functer () {return name; }, setname: function (newName) {name = newName; }}} (); console.log (persona.name); // Acceso directo, el resultado está indefinido console.log (persona.getName ()); // PERSONA PERSONAL.SETNAME ("JOZO"); console.log (persona.getName ()); // JOZO3. Implementar objetos orientados
De esta manera, diferentes objetos (instancias de clases) tienen miembros y estados independientes y no interfieren entre sí. Aunque no existe tal mecanismo como clase en JavaScript, mediante el uso de cierres, podemos simular tales mecanismos. Hablemos del ejemplo anterior:
function persona () {var name = "predeterminado"; return {getName: functer () {return name; }, setname: function (newName) {name = newName; }}}; var persona1 = persona (); print (persona1.getName ()); John.setName ("Person1"); print (persona1.getName ()); // Person1 var persona2 = persona (); print (persona2.getName ()); Jack.SetName ("Erson2"); imprimir (erson2.getName ()); // Person2¡Las dos instancias de la persona Person1 y Person2 no interfieren entre sí! Porque estas dos instancias tienen acceso independiente al miembro del nombre.
5. Fugas de memoria y soluciones
Mecanismo de reciclaje de basura
Hablando de la gestión de la memoria, es naturalmente inseparable del mecanismo de recolección de basura en JS. Hay dos estrategias para realizar la recolección de basura: marcado autorización y conteo de referencias ;
Extracción de la marca: cuando se ejecuta el recolector de basura, marcará todas las variables almacenadas en la memoria. Luego, eliminará las etiquetas de variables en el entorno y las etiquetas de variables a las que se hace referencia por variables en el entorno. Después de eso, si la variable está marcada nuevamente, significa que la variable está lista para eliminarse. Hasta 2008, IE, Firefox, Opera, Chrome y JavaScript de Safari han utilizado este método;
Recuento de referencia: rastrear el número de veces que se hace referencia a cada valor. Cuando se declara una variable y se asigna un valor de un tipo de referencia a la variable, el número de veces que se hace referencia a este valor es 1. Si este valor se asigna a otra variable, el número de veces una referencia se incrementa en 1. Por el contrario, si una variable se desvía de la referencia de la referencia del valor, el número de referencias del valor se reduce por 1, y cuando el número de veces es 0, será una espera a la referencia de Garb;
Un gran problema con este método es la referencia circular, es decir, el objeto A contiene un puntero a B, y el objeto B también contiene una referencia a A. Esto puede hacer que una gran cantidad de memoria se reciclara (filtraciones de memoria), porque sus referencias nunca pueden ser 0. En las primeras versiones de IE (IE4-II6) adoptaron un mecanismo de recolección de rotura que se contaba. Una de las razones por las cuales los cierres causaron fugas de memoria fueron un defecto de este algoritmo.
Sabemos que algunos objetos en IE no son objetos nativos de JavaScript. Por ejemplo, los objetos en BOM y DOM se implementan en forma de objetos COM, y el mecanismo de recolección de basura de los objetos COM adopta el recuento de referencias. Por lo tanto, aunque el motor JavaScript de IE adopta una estrategia de compensación de etiquetas, acceder a los objetos COM todavía se basa en el recuento de referencias, por lo que mientras los objetos COM se diseñen en IE, ¡habrá un problema de referencias circulares!
Toma una castaña:
Window.Onload = function () {var el = document.getElementById ("id"); el.onclick = function () {alert (el.id);}}¿Por qué este código causa filtraciones de memoria?
el.OnClick = function () {alert (el.id);};Al ejecutar este código, el objeto de función anónima se asigna al atributo OnClick de El; Luego, la función anónima se refiere al objeto EL en el interior, y hay una referencia circular, por lo que no se puede reciclar;
Solución:
Window.Onload = function () {var el = document.getElementById ("id"); var id = el.id; // unreference el.OnClick = function () {alert (id); } el = nulo; // Borrar el objeto activo en la función externa a la referencia por el cierre}Lo anterior es el resumen del conocimiento relevante sobre la fuga de memoria de recolección de basura de la cadena de alcance de cierre de JS que le presenta el editor. ¡Espero que sea útil para todos!