开发者

Javascript memory leaks: why would assigning object to null work?

Can someone scratch an itch for me in regards to the nature of assignment-to-null fix used to prevent memory leaks?

We are all familiar with the following technique to stop the circular reference between the DOM object and the JS object, in order to prevent memory leaks:

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

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

      ele = null;
    }

The question is why would the above work? Setting "ele" to null will definitely stop the circular references, but wouldn't it also prevent future references to "ele"?

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

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

      ele = null;

    }

And yet the event listener fires. It will complain that "ele" object is null (which is what we would expect).

Given the above behavior, are we right to deduce that the Javascript engine implemention will hold some sort of internal reference to the event listener, and it is this reference that is called when the event is triggered?

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

If there were to be a reference like the above, wouldn't the assignment-to-null fix be implementation dependent? Or, is it part of the ECMAScript specification.

From my understanding, this fix has always been cross browser safe. I haven't come across many examples that makes specific mentions on detecting/sniffing the browser type before applying the null assignment.

===============EDIT==================

I think due to the way I've laid out the question may have unwittingly directly the discussion from what I was trying to convey. A couple of concepts that are being referenced:

object handles/object references 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. 

The above isn't where my itch lies, and I regret adding the line:

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

The above makes this look a closure question. I fully e开发者_C百科xpected the error to be thrown as indicated in the original post.

My itch is more along the context of ... for some reason, in my mind, I keep thinking that Javascript engine will call the ele.onlick() directly when the event fires and setting the element to null is akin to the following flow:

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

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

Given that we know in the original post that the event handler still fires after ele has been set to null, in my mind the flow is more akin to:

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

var objRef = obj; 

obj = null;  

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

My itch comes down to the question, is the above how most Javascript implementation work, where some internal reference is pointing to the event handler assigned, and it is this internal reference is called when the event is triggered?

Or phrased differently, what is stopping a Javascript Engine implementation from calling an ele.onclick() directly (setting aside for the moment the design and architecture issues)?

Maybe my thought processes works differently, but didn't anyone else take a second look when they encountered the assignment-to-null fix for the first time, where the element reference was null-ed and yet the code for the handler was still be executed?


Trashing all the old answer, and addressing the edit :)

Let's take an easier example: textContent.

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

This code sets the textContent of #foo to abc, and discards the reference to ele. Now what happens if I ask for #foo again?...

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

It will log "#foo is abc". This simply indicates that #foo lives on, outside my script, and that document keeps a reference to it (since I got it back calling a method on document). It works the same with event handlers.

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

Event handlers are not a special kind of property. They're just variables on which you write references (to functions). These are roughly functional language features, where functions can be used as simple values. The JavaScript implementation simply calls event handlers from the DOM reference, instead of from the ele reference. Let's make another simpler example, without 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();

As you can see, functions are not tied to the references on which we assign them: they're tied to the objects the references point to. You can call them from any reference to the object, not just from the one you've used to assign the function.

As such, you can nullify references to break circular dependencies without affecting the correct behavior of objects.


I'm far from a JavaScript guru; but I think I can at least partially answer your question simply by providing a reminder of the difference between a variable and an object. Let's step through your code one line at a time.

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

Here we are doing two things:

  • Declaring a variable (ele)
  • Assigning a reference to a DOM element (an object) to that variable

Next:

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

Here we are using the variable ele to assign a callback to the onclick event of the object (again, a DOM element) which it references.

Finally:

ele = null;

At last we assign a null reference to our ele variable. It no longer references the object it did just moments ago. However, that object is still there, with the same onclick handler; it has not changed.

The fact that the handler still gets called is correct behavior. Now, as for the error, let's take another look at the function assigned to that DOM element's onclick event:

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

This ele is the very same variable to which we just assigned null. And thus when this function is called--because, again, the onclick handler attached to the object has not disappeared--a null reference exception is thrown.


Since you get ele in the first place by calling document.getElementById("someParagraphId"), the object that it references already exists in memory before you assign ele. It's a part of the document -- of course there is a reference to it in memory other than ele.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜