2010-06-17 8 views
9

Pregunta: Parece que hay muchos beneficios para los cierres, pero ¿cuáles son los aspectos negativos (pérdida de memoria, problemas de ofuscación, aumento del ancho de banda)? Además, ¿es correcto mi entendimiento de Closures? Finalmente, una vez que se crean cierres, ¿pueden destruirse?Cierres de Javascript: ¿Cuáles son los aspectos negativos?

He estado leyendo un poco acerca de los cierres de Javascript. Espero que alguien un poco más informado guíe mis afirmaciones, corrigiéndome donde está mal.

Beneficios de Cierre:

  1. encapsular las variables a un ámbito local, mediante el uso de una función interna. El anonimato de la función es insignificante.

Lo que he encontrado es útil para hacer algunas pruebas básicas, en cuanto a/alcance global local:

<script type="text/javascript"> 

    var global_text = ""; 
    var global_count = 0; 
    var global_num1 = 10; 
    var global_num2 = 20; 
    var global_num3 = 30; 

    function outerFunc() { 

     var local_count = local_count || 0; 

     alert("global_num1: " + global_num1); // global_num1: undefined 
     var global_num1 = global_num1 || 0; 
     alert("global_num1: " + global_num1); // global_num1: 0 

     alert("global_num2: " + global_num2); // global_num2: 20 
     global_num2 = global_num2 || 0;   // (notice) no definition with 'var' 
     alert("global_num2: " + global_num2); // global_num2: 20 
     global_num2 = 0; 

     alert("local_count: " + local_count); // local_count: 0 

     function output() { 
     global_num3++; 

     alert("local_count: " + local_count + "\n" + 
       "global_count: " + global_count + "\n" + 
       "global_text: " + global_text 
      ); 

     local_count++; 
     } 

     local_count++; 
     global_count++; 

     return output; 
    } 

    var myFunc = outerFunc(); 

    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 1 
     * global_count: 1 
     * global_text: 
     **********************/ 

    global_text = "global"; 
    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 2 
     * global_count: 1 
     * global_text: global 
     **********************/ 

    var local_count = 100; 
    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 3 
     * global_count: 1 
     * global_text: global 
     **********************/ 


    alert("global_num1: " + global_num1);  // global_num1: 10 
    alert("global_num2: " + global_num2);  // global_num2: 0 
    alert("global_num3: " + global_num3);  // global_num3: 33 

</script> 

cosas interesantes que llevaron a cabo de la misma:

  1. El las alertas en outerFunc solo se llaman una vez, que es cuando la llamada outerFunc se asigna a myFunc (myFunc = outerFunc()). Esta asignación parece mantener el outerFunc abierto, en lo que me gustaría llamar un estado persistente.

  2. Cada vez que se invoca myFunc, se ejecuta la devolución. En este caso, el retorno es la función interna.

  3. Algo realmente interesante es la localización que se produce al definir las variables locales. Observe la diferencia en la primera alerta entre global_num1 y global_num2, incluso antes de que la variable intente crearse, global_num1 se considera indefinido porque la 'var' se usó para indicar una variable local para esa función. - Esto ya se ha mencionado anteriormente, en el orden de operación del motor Javascript, es agradable ver que esto funcione.

  4. Se pueden usar globos globales, pero las variables locales los anularán. Observe antes de la tercera llamada myFunc, se crea una variable global llamada local_count, pero no tiene ningún efecto en la función interna, que tiene una variable con el mismo nombre. Por el contrario, cada llamada a función tiene la capacidad de modificar variables globales, como lo notó global_var3.

Pon Pensamientos: A pesar de que el código es sencillo, que está lleno de alertas para ustedes, para que pueda conectar y jugar.

Sé que hay otros ejemplos de cierres, muchos de los cuales usan funciones anónimas en combinación con estructuras de bucle, pero creo que esto es bueno para un curso de 101 iniciadores para ver los efectos.

Lo que más me preocupa es el impacto negativo que los cierres tendrán en la memoria. Debido a que mantiene abierto el entorno de funciones, también mantiene esas variables almacenadas en la memoria, lo que puede/no tener implicaciones en el rendimiento, especialmente con respecto a los recorridos DOM y la recolección de basura. Tampoco estoy seguro de qué tipo de rol jugará esto en términos de pérdida de memoria y no estoy seguro de si el cierre se puede eliminar de la memoria con un simple "eliminar myFunc ;."

Espero que esto ayude a alguien,

vol7ron

+3

Estoy sacando algunas palomitas de maíz para esta. – Pointy

Respuesta

6

Usted puede obtener una serie de buenas respuestas. Una cierta negativo es la pérdida de memoria referencia circular Internet Explorer. Básicamente, 'circulares' referencias a objetos DOM son no se reconoce como coleccionable por JScript. es fácil crear lo que IE considera una referencia circular usando los cierres. Varios ejemplos se proporcionan en el segundo link.

en IE6, la única manera de recuperar la memoria es dar por terminado todo el proceso. En IE7 lo mejoraron de modo que cuando navegas fuera de la página en cuestión (o la cierras), la memoria se recupera. En IE8, los objetos DOM son mejor entendidos por JScript y se recopilan como es de esperar que sean.

La solución sugerida para IE6 (además de finalizar el proceso) no es utilizar cierres.

+1

No hubo muchas respuestas, por lo que obtiene el voto. Aunque no estoy demasiado preocupado por IE6. De acuerdo con (http://www.w3schools.com/browsers/browsers_stats.asp), todavía se usa en un 7%, pero creo que se reducirá drásticamente en los próximos meses. El gobierno es un gran contribuyente a las versiones anteriores de IE, una vez que utilizan la nueva versión, muchos de los contratistas privados probablemente dejarán de apoyarlo/usarlo también. Creo que en los últimos meses muchas agencias gubernamentales han cambiado a un navegador más nuevo y más seguro, especialmente después de esa amenaza de Adobe/IE6. – vol7ron

+0

Estoy un poco sorprendido por la falta de respuestas también. Considero tu punto en IE6; pero, por desgracia, todavía veo un 25% de IE6 entre los visitantes de las aplicaciones web centradas en la empresa en las que trabajo. Algunas de estas grandes empresas son tan conservadoras que todavía están ordeñando el "costo hundido" de su inversión en Windows XP, e IE6 junto con ella. Y están disminuyendo, en diversos grados, los esfuerzos progresivos de los proveedores basados ​​en la web que, no obstante, todavía quieren su negocio. Realmente espero que desaparezca en los próximos meses, pero no me sorprendería si hay grandes empresas que aún ejecutan IE6 en 2015. –

0

Los cierres pueden causar pérdidas de memoria, sin embargo, Mozilla ha intentado optimizar su motor de recolección de basura para evitar esto.

No estoy seguro de cómo Chrome maneja los cierres. Creo que están a la par con Mozilla, pero no quiero decirlo con certeza. IE8 definitivamente ha mejorado con respecto a las versiones anteriores de IE; es casi un navegador completamente nuevo, todavía hay algunos matices.

También debe comparar el código para ver si hay alguna mejora en la velocidad.

6

Los cierres traen muchos beneficios ... pero también un número de trampas. Lo mismo que los hace poderosos también los hace bastante capaces de hacer un lío si no tienes cuidado.

Además del problema con las referencias circulares (que en realidad ya no es un gran problema, ya que IE6 apenas se usa fuera de China), hay al menos otro gran potencial negativo: Pueden complicar el alcance. Cuando se usan bien, mejoran la modularidad y la compatibilidad al permitir que las funciones compartan datos sin exponerlo ... pero cuando se usan mal, puede ser difícil, sino imposible, rastrear exactamente dónde se establece o cambia una variable.

JavaScript sin cierres tiene tres * ámbitos para las variables: nivel de bloque, nivel de función y global. No hay alcance de nivel de objeto. Sin cierres, usted sabe que una variable se declara en la función actual o en el objeto global (porque es donde viven las variables globales).

Con cierres, ya no tiene esa seguridad. Cada función anidada introduce otro nivel de alcance, y cualquier cierre creado dentro de esa función ve (principalmente) las mismas variables que la función que lo contiene. El gran problema es que cada función puede definir sus propias variables a voluntad que oculten las externas.

El uso de cierres correctamente requiere que (a) tenga en cuenta cómo los cierres y var afectan el alcance, y (b) controle en qué ámbito se encuentran sus variables. De lo contrario, las variables pueden compartirse accidentalmente (o perder pseudovariables) !), y todo tipo de extravagancia puede sobrevenir.


Considere este ejemplo:

function ScopeIssues(count) { 
    var funcs = []; 
    for (var i = 0; i < count; ++i) { 
     funcs[i] = function() { console.log(i); } 
    } 
    return funcs; 
} 

corto, sencillo ... y casi con seguridad roto. Mire:

x = ScopeIssues(100); 

x[0](); // outputs 100 
x[1](); // does too 
x[2](); // same here 
x[3](); // guess 

Cada función en la matriz produce count. ¿Que está pasando aqui? Está viendo los efectos de combinar cierres con un malentendido de variables y alcance cerrados.

Cuando se crean los cierres, no están utilizando el valor de i en el momento en que se crearon para determinar qué salida. Están utilizando la variable i, que se comparte con la función externa y aún está cambiando. Cuando lo emiten, están emitiendo el valor en el momento en que es llamado. Eso será igual a count, el valor que hizo que el ciclo se detenga.

Para solucionar esto, necesitará otro cierre.

function Corrected(count) { 
    var funcs = []; 
    for (var i = 0; i < count; ++i) { 
     (function(which) { 
      funcs[i] = function() { console.log(which); }; 
     })(i); 
    } 
} 

x = Corrected(100); 

x[0](); // outputs 0 
x[1](); // outputs 1 
x[2](); // outputs 2 
x[3](); // outputs 3 

Otro ejemplo:

value = 'global variable'; 

function A() { 
    var value = 'local variable'; 
    this.value = 'instance variable'; 
    (function() { console.log(this.value); })(); 
} 

a = new A(); // outputs 'global variable' 

this y arguments son diferentes; a diferencia de casi todo lo demás, son no compartidos a través de los límites de cierre ?. Cada llamada a la función que redefine - ya menos que se llama a la función como

  • obj.func(...),
  • func.call(obj, ...),
  • func.apply(obj, [...]) o
  • var obj_func = func.bind(obj); obj_func(...)

para especificar un this, entonces Obtendrá el valor predeterminado para this: el objeto global. ^

La expresión más común de conseguir alrededor de la cuestión this es declarar una variable y establezca su valor en this. Los nombres más comunes que he visto son that y self.

function A() { 
    var self = this; 
    this.value = 'some value'; 
    (function() { console.log(self.value); })(); 
} 

Pero eso hace que self una variable real, con toda la rareza potencial que conlleva. Afortunadamente, es raro querer cambiar el valor de self sin redefinir la variable ... pero dentro de una función anidada, la redefinición de self, por supuesto, la redefine para todas las funciones anidadas dentro de ella. Y no se puede hacer algo como

function X() { 
    var self = this; 
    var Y = function() { 
     var outer = self; 
     var self = this; 
    }; 
} 

debido a la elevación . JavaScript efectivamente mueve todas las declaraciones de variables a la parte superior de la función.Eso hace que el código anterior equivale a

function X() { 
    var self, Y; 
    self = this; 
    Y = function() { 
     var outer, self; 
     outer = self; 
     self = this; 
    }; 
} 

self ya es una variable local antes de outer = self carreras, por lo outer obtiene el valor local - que en este momento, es undefined. Acaba de perder su referencia al exterior self.


* A partir de ES7. Anteriormente, solo había dos, y las variables eran aún más fáciles de rastrear. : P

? Las funciones declaradas usando la sintaxis lambda (nueva para ES7) no redefinen this y arguments. Lo que potencialmente complica la cuestión aún más.

^Los intérpretes más nuevos admiten el llamado "modo estricto": una función de aceptación que tiene como objetivo hacer que ciertos patrones de códigos dudosos fallen por completo o causen menos daños. En modo estricto, this se configura de manera predeterminada en undefined en lugar del objeto global. Pero sigue siendo un valor completamente distinto de lo que usualmente intentaba meter.

+1

Muy bien, pero creo que tiene un pequeño error tipográfico en el último bit. "' outer' ya es una variable local ... "¿Creo que el' outer' debería ser 'self'? –

+0

@JakeKing: Ups ... corregidos. Gracias :) – cHao

+0

upvote para el claro ejemplo de elevación que causa un comportamiento inesperado. –

Cuestiones relacionadas