Search code examples
javascriptjquerybackbone.js

Impossible Backbone Zombies


I've been trying to debug my Backbone multi-page app for most of the day now to get rid of 'zombies', but unfortunately to no avail. Before today, I didn't even realize I have a zombie problem. What am I doing wrong?

This is my RegionManager:

  var regionManager = (function() {
    var currView = null;
    var rm = {};

    var closeView = function(view) {
      if (view && view.close) {
        view.close();
      }
    };

    var openView = function(view) {
      view.render();
      if (view.onShow) {
        view.onShow();
      }
    };

    rm.show = function(view) {
      closeView(currView);
      currView = view;
      openView(currView);
    };

    return rm;
  })();

This is my View cleaning up function:

  Backbone.View.prototype.close = function() {
    if (this.onClose) {
      this.onClose();
    }

    if (this.views) {
      _.invoke(this.views, 'close');
    }

    // Unbind any view's events.
    this.off();

    // Unbind any model and collection events that the view is bound to.
    if (this.model) {
      this.model.off(null, null, this);
    }

    if (this.collection) {
      this.collection.off(null, null, this);
    }

    // Clean up the HTML.
    this.$el.empty();
  };

I tried appending the View els directly to the body and using this.remove(); in the View clean-up function (instead of using a common el: $('#content') to which I am appending elements, then cleaning up by this.$el.empty()), but that didn't work either.

It might have something to do with my "global Events":

Backbone.Events.on('letterMouseDown', this.letterMouseDown, this);

But I take care of them with the onClose function:

onClose: function() {
  Backbone.Events.off('letterMouseDown');
}

Solution

  • I'm pretty sure I found the root for my problem.

    mu is too short was right, with the close() method I wasn't removing the events bound directly to my el (which I tried to do by this.off() - this.$el.off()/this.undelegateEvents() is the correct way). But for me, it only fixed the problem that events got called multiple times unnecessarily.

    The reason I was plagued by 'zombie views' or unintended behavior was that I wasn't freeing up the memory in the View..

    this.remove() only gets rid of the el and it's elements/events, but not the View's internal variables. To elaborate - in my View I have an array declared like so this.array: [] and I didn't have it freed in the onClose function.

    All I had to do was empty it in the onClose function or initially declare the array as this.array: null so on recurrent View renderings it would at least free the previous array (it still should be freed on the onClose method though, because the array/object is still going to sit in the memory until browsing away from the page).

    It was excruciating to debug, because it's a crossword game (at least my code is hard to read there) and sometimes the words didn't match up, but I didn't know where the problem was coming from.

    Lessons learned.