Introducción
Un cierre es una función que tiene permiso para acceder a las variables en otro alcance de la función.
Los cierres son difíciles de entender en JavaScript. Muchas aplicaciones avanzadas confían en los cierres para implementarlas. Primero veamos un ejemplo a continuación:
function outer () {var i = 100; function inner () {console.log (i); }}En el código anterior, de acuerdo con el alcance de la variable, todas las variables locales en la función externa son visibles para la función interna; Las variables locales en la función interna son invisibles fuera de la función interna, por lo que las variables locales en la función interna no se pueden leer fuera de la función interna.
Dado que la función interna puede leer las variables locales de la función externa, siempre y cuando el interior se use como valor de retorno, las variables locales internas se pueden leer directamente fuera del oer.
function outer () {var i = 100; function inner () {console.log (i); } return inner;} var rs = outer (); rs ();Esta función tiene dos características:
Después de ejecutar var rs = outer () de esta manera, la RS real apunta a la función interna. Este código es en realidad un cierre. Es decir, cuando la función interna dentro de la función externa se hace referencia por una variable fuera de la función externa, se crea un cierre.
Alcance
En pocas palabras, el alcance es el rango accesible de variables y funciones, es decir, el alcance controla la visibilidad y el ciclo de vida de las variables y funciones. En JavaScript, el alcance de las variables es global y local.
Alcance global
var num1 = 1; function divers1 () {num2 = 2;}Los tres objetos anteriores Num1, Num2 y Fun1 son ámbitos globales. Cabe señalar aquí que las variables que definen asignaciones directas al final se declaran automáticamente como alcanzos globales;
Alcance local
function wrap () {var obj = "Estoy envuelto por envoltura, y el exterior de la envoltura no puede acceder directamente a mí"; función innerfun () {// El exterior no puede acceder a mí}}Cadena de alcance
Todo en JavaScript es un objeto. Estos objetos tienen una propiedad [[alcance]], que contiene una colección de objetos en el alcance creado por la función. Esta colección se llama la cadena de alcance de la función, que determina a qué datos pueden acceder por la función.
función add (a, b) {return a+b;}Cuando se crea una función, su propiedad [[alcance]] agregará automáticamente el alcance global
var sum = add (3,4);
Cuando se llama a una función, se crea un objeto interno llamado contexto de ejecución. Este objeto Z define el entorno cuando se ejecuta la función. También tiene su propia cadena de alcance para la resolución del identificador, y su cadena de alcance se inicializa como un objeto contenido en [[Alcance]] de la función de ejecución actual.
Durante la ejecución de la función, cada vez que se encuentra una variable, se pasará un proceso de análisis de identificadores para decidir dónde obtener y almacenar datos. Este proceso comienza desde la cabeza de la cadena de alcance, es decir, busca un identificador del mismo nombre del objeto activo. Si se encuentra, use la variable correspondiente a este identificador. Si no se encuentra, continúe buscando el siguiente objeto en la cadena de alcance, si se buscan todos los objetos (el último es un objeto global) no se encuentran, el identificador se considera indefinido.
Cierre
Un cierre es simplemente una función que accede a sus variables externas.
var quo = function (status) {return {getStatus: function () {return status; }}}El estado se guarda en quo, devuelve un objeto, el método getStatus en este objeto se refiere a la variable de estado, es decir, la función getStatus accede a su estado de variable externa;
var newValue = quo ('string'); // devuelve un objeto anónimo, referenciado por NewValue con newValue.getStatus (); // Accedó el estado de la variable interna de quoSi el método GetStatus no está disponible, el estado se reciclará automáticamente después de quo ('sting'). Es precisamente porque el objeto anónimo devuelto es referenciado por un objeto global, y el objeto anónimo depende del estado, por lo que evitará la liberación de estado.
ejemplo:
// esquema de error var test = function (nodos) {var i; for (i = 0; i <nodo.length; i ++) {nodos [i] .Onclick = function (e) {alerta (i); }}}Una función anónima crea un cierre, y el acceso a I es I en la función de prueba externa, por lo que cada nodo en realidad se refiere a la misma i.
// Solución de mejora var test = function (nodos) {var i; for (i = 0; i <nodo.length; i ++) {nodos [i] .Onclick = function (i) {return function () {alert (i); }; }(i); }}Cada nodo está vinculado a un evento. Este evento recibe un parámetro y se ejecuta de inmediato, pasando en i. Debido a que se pasa por valor, cada bucle generará una nueva copia de seguridad para la actual I.
El papel del cierre
function outer () {var i = 100; function inner () {console.log (i ++); } return inner;} var rs = outer (); rs (); // 100rs (); // 101rs (); // 102En el código anterior, RS es la función interna de cierre. RS corrió tres veces en total, la primera vez fue 100, la segunda vez fue 101, y la tercera vez fue 102. Esto muestra que la variable local I en la función exterior se ha mantenido en la memoria y no se eliminó automáticamente cuando se le llamó.
El propósito del cierre es que después de completar y devolver la ejecución externa, el cierre hace que el mecanismo de recolección de basura del Javascript (recolección de captación) no recicle la memoria ocupada por el exterior, porque la ejecución de la función interna del exterior depende de las variables en el exterior. (Otra explicación: Exterior es la función principal del interior, el interno se asigna a una variable global, lo que hace que el interno esté en la memoria todo el tiempo, y la existencia del interior depende de un exterior, porque algo externo siempre está en la memoria y no se recolectará basura y se recicla después de que la llamada esté terminada).
El cierre tiene permiso para acceder a todas las variables dentro de la función.
Cuando una función devuelve un cierre, el alcance de la función se guardará en la memoria hasta que el cierre no exista.
Cierres y variables
Debido al mecanismo de la cadena de alcance, el cierre solo puede obtener el último valor que contiene cualquier variable en la función. Vea el siguiente ejemplo:
función f () {var rs = []; for (var i = 0; i <10; i ++) {rs [i] = function () {return i; }; } return rs;} var fn = f (); for (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () valor de retorno:' + fn [i] ());}Las funciones devolverán una matriz. En la superficie, parece que cada función debería devolver su propio valor de índice. De hecho, cada función regresa 10. Esto se debe a que la cadena de alcance de la primera función contiene los objetos activos de la función F, y se refieren a la misma variable i. Cuando la función F devuelve, el valor de la variable I es 10. En este momento, cada función guarda el mismo objeto variable de variable i. Podemos obligar al cierre a comportarse como se esperaba creando otra función anónima.
función f () {var rs = []; for (var i = 0; i <10; i ++) {rs [i] = function (num) {return function () {return num; }; }(i); } return rs;} var fn = f (); for (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () valor de retorno:' + fn [i] ());}En esta versión, en lugar de asignar el cierre directamente a la matriz, definimos una función anónima y asignamos el resultado de ejecutar inmediatamente la función anónima a la matriz. Aquí, la función anónima tiene un parámetro num. Al llamar a cada función, pasamos la variable i. Dado que los parámetros se pasan por valor, la variable I se copiará al parámetro num. Dentro de esta función anónima, se crea y devuelve un número de acceso de cierre. De esta manera, cada función en la matriz RS tiene una copia de su propia variable NUM, por lo que se pueden devolver diferentes valores.
Este objeto de cierre
var name = 'jack'; var o = {name: 'bingdian', getName: function () {return function () {return this.name; }; }} console.log (o.getName () () ()); // jackvar name = 'jack'; var o = {name: 'bingdian', getName: function () {var self = this; Función de retorno () {return self.name; }; }} console.log (o.getName () () ()); // bingdianFiltración de memoria
function asignHandler () {var el = document.getElementById ('demo'); el.OnClick = function () {console.log (el.id); }} asignHandler ();El código anterior crea un cierre como controlador de eventos El Elemento, y este cierre crea una referencia circular. Mientras exista la función anónima, el número de referencias EL es al menos 1, porque la memoria que ocupa nunca se reciclará.
function asignHandler () {var el = document.getElementById ('demo'); var id = el.id; el.OnClick = function () {console.log (id); } el = null;} asignHandler ();Configurar la variable El NULL puede desreferencia el objeto DOM y asegurarse de que consuma memoria normalmente.
Imitar el alcance de nivel de bloque
La declaración establecida en cualquier par de aparatos ortopédicos rizados ({y}) pertenece a un bloque, y todas las variables definidas en esto son invisibles fuera del bloque de código, que llamamos alcance a nivel de bloque.
(function () {// alcance de nivel de bloque}) ();Aplicación de cierres
Proteger la seguridad de las variables en la función. Como en el ejemplo anterior, solo la función interna puede acceder a I en la función exterior, pero no se puede acceder a través de otros canales, protegiendo así la seguridad de i.
Mantener una variable en la memoria. Como en el ejemplo anterior, debido al cierre, la I en la función exterior siempre existe en la memoria, por lo que cada vez que se ejecuta RS (), se me agregará 1.