2011-06-13 8 views
6

¿Puede alguien rascarme la comezón en lo que respecta a la naturaleza de la solución de asignación a nulo utilizada para evitar fugas de memoria?Fugas de memoria Javascript: ¿por qué asignar el objeto al trabajo nulo?

Todos estamos familiarizados con la técnica siguiente para detener la referencia circular entre el objeto y el objeto DOM JS, con el fin de evitar pérdidas de memoria:

function foo() { 
     var ele = document.getElementById("someParagraphId"); 

     ele.onclick = function() { 
     //some action here 
     }; 

     ele = null; 
    } 

La pregunta es ¿por qué el trabajo anterior? Poner "ele" en nulo definitivamente detendrá las referencias circulares, pero ¿no impediría futuras referencias a "ele"?

function foo() { 
     var ele = document.getElementById("someParagraphId"); 

      ele.onclick = function() { 
       console.log("accessing ele ... after set to null " + ele.onclick); 
      }; 

     ele = null; 

    } 

Y sin embargo, el detector de eventos se dispara. Se quejará de que el objeto "ele" es nulo (que es lo que esperaríamos).

Dado el comportamiento anterior, ¿estamos en lo cierto al deducir que la implementación del motor de Javascript contendrá algún tipo de referencia interna al detector de eventos, y es esta referencia la que se invoca cuando se desencadena el evento?

eventHandlerRef = //assignment to "ele.onclick" handler/listener; 

Si hubiera una referencia como la anterior, ¿no sería la implementación a null fix dependiente de la implementación? O bien, es parte de la especificación ECMAScript.

Desde mi entender, esta solución siempre ha sido segura para navegadores cruzados. No he encontrado muchos ejemplos que hagan menciones específicas sobre la detección/rastreo del tipo de navegador antes de aplicar la asignación nula.

=============== EDITAR ==================

creo que debido a la forma en que he presentado la pregunta puede haber involuntariamente directamente la discusión de lo que estaba tratando de transmitir. Un par de conceptos que se hace referencia:

identificadores de objetos referencias/objeto Ala:

var obj1, obj2; 
    obj1 = {foo: "bar"}; //obj1 points to the an area of the heap allocated 
          //for the object literal. 

obj2 = obj1;  //obj2 points to the same position in the heap 
         //as obj1. 

//obj1 = null or 
obj1 = {/*another obj literal*/} //the new object literal is created 
             //and allocated in some other part 
             //in the heap, and obj1 now points 
             //to the new location. 

//obj2 is unaffected by obj1's re-assignment. 

Lo anterior no es donde radica mi picazón, y lamento añadiendo la línea:

console.log("accessing ele ... after set to null " + ele.onclick); 

Lo anterior hace que esto parezca una pregunta de cierre. Esperaba que el error se lanzara como se indica en la publicación original.

My itch está más en el contexto de ... por alguna razón, en mi mente, sigo pensando que el motor de Javascript llamará directamente al ele.onlick() cuando el evento se dispara y establecer el elemento como nulo es similar al siguiente flujo :

var obj = {}; 
obj.method = function() {}; 

obj = null; 
//error calling obj.method(), i.e., obj is null 

Dado que sabemos en el post original que el controlador de eventos todavía dispara al cabo ele se ha establecido en nulo, en mi mente el flujo es más parecido a:

var obj = {}; 
obj.method = function() {}; 

var objRef = obj; 

obj = null; 

//The Javascript Engine calling objRef.method when event triggered. 

mi picor viene a la pregunta, es el anterior cómo la mayoría de Ja trabajo de implementación de vascript, donde algunas referencias internas apuntan al manejador de eventos asignado, y es esta referencia interna la que se llama cuando se desencadena el evento?

O redactadas de manera diferente, lo que es detener una aplicación Javascript motor de llamar a un ele.onclick() directamente (dejando de lado por el momento el diseño y la arquitectura problemas)?

Tal vez mis procesos de pensamiento funcionen de manera diferente, pero nadie más echó un segundo vistazo cuando encontraron la solución de asignación a nulo por primera vez, donde la referencia del elemento era null y aún el código para el controlador todavía se ejecutará?

+0

He editado mi respuesta para abordar su edición. – zneak

Respuesta

0

Dado que obtiene ele en primer lugar al llamar al document.getElementById("someParagraphId"), el objeto al que hace referencia ya existe en la memoria antes de asignar ele. Es una parte del documento; por supuesto, hay una referencia en la memoria que no sea ele.

6

elimino toda la respuesta de edad, y hacer frente a la edición :)

Tomemos un ejemplo sencillo: textContent.

var ele = document.getElementById('foo'); 
ele.textContent = 'abc'; 
ele = null; 

Este código establece el textContent de #foo-abc, y descarta la referencia a ele. Ahora, ¿qué ocurre si pido #foo de nuevo? ...

var ele = document.getElementById('foo'); 
ele.textContent = 'abc'; 
ele = null; 
ele = document.getElementById('foo'); 
console.log('#foo is ' + ele.textContent); 

Se registrará "#foo is abc". Esto simplemente indica que #foo sigue vivo, fuera de mi secuencia de comandos, y que document mantiene una referencia (desde que lo recuperé llamando a un método en document). Funciona igual con los controladores de eventos.

var ele = document.getElementById('foo'); 
ele.onclick = function() { console.log('abc'); }; 
ele = null; 
ele = document.getElementById('foo'); 
console.log('#foo.onclick is ' + ele.onclick); 

Los controladores de eventos no son un tipo especial de propiedad. Son solo variables en las que escribes referencias (a funciones). Estas son características del lenguaje básicamente funcionales, donde las funciones se pueden usar como valores simples. La implementación de JavaScript simplemente llama a los manejadores de eventos desde la referencia DOM, en lugar de desde la referencia ele. Vamos a hacer otro ejemplo más simple, sin document.getElementById.

var a = {}; 
var b = a; 
b.foo = function() { console.log("hello world!"); }; 
console.log(a.foo + " is the same as " + b.foo); 
b = null; 
console.log("Even though b is " + b + ", a.foo is still " + a.foo); 
a.foo(); 

Como se puede ver, las funciones no están atados a las referencias sobre las que asignarles: son atados a los objetos de las referencias apuntan. Puede llamarlos desde cualquier referencia al objeto, no solo desde el que ha usado para asignar la función.

Como tal, puede anular las referencias para romper las dependencias circulares sin afectar el comportamiento correcto de los objetos.

+0

Creo que puedes explicar los cierres más simplemente que eso. Un cierre está formado por una función interna que tiene acceso a las variables y parámetros de una función externa. El objeto variable de la función externa (que tiene todas sus variables locales y parámetros como propiedades) está en la cadena de alcance de la función interna. La resolución del identificador sigue el patrón típico de comprobación del objeto variable local, luego el siguiente en la cadena de alcance (el cierre), luego el siguiente y así sucesivamente hasta que se alcanza el objeto global. – RobG

0

Estoy lejos de ser un gurú de JavaScript; pero creo que al menos puedo responder parcialmente a su pregunta simplemente proporcionando un recordatorio de la diferencia entre una variable y un objeto . Pasemos por su código una línea a la vez.

var ele = document.getElementById("someParagraphId"); 

Aquí estamos haciendo dos cosas:

  • declarar una variables (ele)
  • Asignación de una referencia a un elemento DOM (un objeto ) a esa variable

Siguiente:

ele.onclick = function() { 
    console.log("accessing ele ... after set to null " + ele.onclick); 
}; 

Aquí estamos usando el variable deele para asignar una devolución de llamada a la onclick caso de la objeto (de nuevo, un elemento de DOM), que hace referencia.

Por último:

ele = null; 

Finalmente asignamos una referencia null a nuestra variable ele. Ya no hace referencia al objeto que hizo hace unos momentos. Sin embargo, ese objeto todavía está allí, con el mismo controlador onclick; no ha cambiado

El hecho de que el manejador sigue siendo llamado es el comportamiento correcto. Ahora, como para el error, vamos a echar otro vistazo a la función asignada a onclick caso de que el elemento DOM:

console.log("accessing ele ... after set to null " + ele.onclick); 

Este ele es la misma variable de a la que acaba de asignar null. Y así, cuando se llama a esta función, porque, de nuevo, el controlador onclick adjunto al objeto no ha desaparecido, se produce una excepción de referencia nula.

Cuestiones relacionadas