3

If I have the following code in two functions of an object:

add: function()
{
    // create trip.
    var trip = new Trip();

    // add the trip using its id.
    this.trips[trip.id] = trip;
},

remove: function(tripId)
{
    // remove trip.
    delete this.trips[tripId];
}

NOTE: The constructor for the Trip object binds a bunch of custom jQuery event handlers to itself.

Will the event handlers bound to the Trip object be automatically destroyed/cleaned up when the Trip object is deleted?

Would the same occur for a DOM node if it was removed and had event handlers bound to it?

Also I read that objects are not cleaned up by the garbage collector until all references to them no longer exist, so do the event handlers bound to the object by itself count as references and prevent the object from being cleaned up, even when I am no longer referencing it?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Gavin
  • 180
  • 3
  • 10
  • Could you paste the part of your constructor where the custom event is attached to your Trip object? – Anurag Jun 22 '10 at 20:38

3 Answers3

5

The event will not be deleted as jQuery maintains a central repository of all bound event handlers, and does know if or when you deleted an associated object using delete. Try this little test to confirm. (jQuery 1.4.2 only)

jsfiddle link:

// 1. a regular JS object
var root = {};
// Since we can't delete anything created with var (except on Chrome),
// we use an object property here. An array value works just as well,
// which is already the case in your example.
root.o = {};

// 2. at this point, jQuery creates an internal property
// jQuery<UNIQ_ID>, for example jQuery1277242840125 inside object o
$(root.o).bind("myEvent", function() { alert("here"); });

// 3. get the *internal* index jQuery assigned this object:
// there's only 1 property, so we just enumerate and fetch it.
var internalIndex;
for(var prop in root.o) {
    internalIndex = root.o[prop];
}

// 4. delete the object
delete root.o;

// 5. query jQuery internal cache with the internal index from step 3
console.log(jQuery.cache[internalIndex].events);
​

Step 5 should log an array of all event types that were associated with the ex-o object, including "myEvent", and it's associated handler, so no the bound events handlers will not delete automatically. Here's what I see get logged (stripped out irrelevant properties):

▾ Object
  ▾ myEvent: Array (1)
    ▾ 0: Object
      ▸ handler: function () { alert("here"); }
        namespace: ""
        type: "myEvent"
      length: 1

The object deletion, however, is not affected, and that will be deleted as expected. However, it is a hole in the wall kind of a situation since there is associated data somewhere in jQuery's cache that will remain there.

It seems that although you can bind events to plain JavaScript objects, you cannot unbind them. It appears jQuery assumes the object is a DOM node when unbinding, and throws the following error:

Uncaught TypeError: Object #<an Object> has no method 'removeEventListener'

Even the part about being able to bind events to objects is, I believe, undocumented. So you have to be a little careful on this, as the following will not work when trying to clean up the event handler references for that object:

$(object).remove()
$(object).unbind(..)

As a workaround, when cleaning up the Trip object, you can explicitly call removeData to do the job.

$(object).removeData();

As I've already mentioned, it's getting knee-deep with jQuery's internals, so you might want to look at an alternative solution, or be wary that library upgrades can easily break your code, which is not very unlikely.

Anurag
  • 140,337
  • 36
  • 221
  • 257
  • [`$(object).removeData();`](http://api.jquery.com/removeData) is the solution. This will remove any data stored in the jQuery data cache (which includes then event handles) for the object. – gnarf Jun 22 '10 at 22:51
  • Thanks, I will test this in the morning and approve the answer if it works. What you have stated about JQuery maintaining a central repository of the events concerns me as this means it is likely the object is not getting deleted either. Please see the following regarding deleting objects: http://stackoverflow.com/questions/742623/deleting-objects-in-javascript if any reference remains to an object it seems the garbage collector will not clean it up, thus if JQuery is storing information about the event and what it is bound to, it could be referencing the object and block collection? – Gavin Jun 23 '10 at 00:51
  • Given the above I think internalIndex might also be blocking collection as it references a property of the object? It might be safer to manually inspect $.cache via firebug. – Gavin Jun 23 '10 at 01:02
  • 1
    @Gavin - As far as I know, jQuery does not hold a **direct** references to objects in this case, but I may be wrong about this. Unless the bound event handler is referring back to the object, it should get deleted and gc'd without problems. Otherwise, you'd have to delete both (event handler, object) to have it garbage collected. Also `internalIndex` in the above case does not interfere with anything since it merely holds an integer value `o[prop]`. – Anurag Jun 23 '10 at 01:56
0

As far as I know, you can only bind event handlers to nodes, or, in special cases, the window, document, etc. For DOM nodes, the event handlers will be removed. Even if they weren't, they wouldn't be able to be triggered anyway. Deleting the object will remove the event handlers associated with it. The event handlers should not prevent the object from being garbage collected.

Chris Laplante
  • 29,338
  • 17
  • 103
  • 134
0

Would the same occur for a dom node if it was removed and had event handlers bound to it?

this.handlerClick = function () { ... };

$(this.testDomNode).bind('click', this.handlerClick);

this.testDomNode.parentNode.removeChild(this.testDomNode);

Using the above code and testing with FireQuery in FireFox removing the dom node does not unbind the handler from the event,

it seems you have to explicitly unbind the handler before removing the dom node as follows:

$(this.testDomNode).unbind('click', this.handlerClick);

this.testDomNode.parentNode.removeChild(this.testDomNode);
Gavin
  • 180
  • 3
  • 10