Prototype Protips #2 - Preventing Memory Leaks & Creating Objects

It’s been almost a year since I posted Prototype Protips #1 (and since then I have grown quite fond of jQuery), but prototype remains an excellent, choice for elegant javascript development still today.

If you aren’t taking advantage of an advanced javascript library, then you are not only missing out on cleaner code, but you are quite possibly likely introducing memory leaks through unfreed event handlers!  IE6 is notoriously bad at this, and you have to take extra caution in EVERY event handler to make sure you aren’t leaking memory.

Douglas Crockford, describes memory leaks in IE like this:

When a DOM object contains a reference to a JavaScript object (such an event handling function), and when that JavaScript object contains a reference to that DOM object, then a cyclic structure is formed. This is not in itself a problem. At such time as there are no other references to the DOM object and the event handler, then the garbage collector (an automatic memory resource manager) will reclaim them both, allowing their space to be reallocated. The JavaScript garbage collector understands about cycles and is not confused by them. Unfortunately, IE's DOM is not managed by JScript. It has its own memory manager that does not understand about cycles and so gets very confused. As a result, when cycles occur, memory reclamation does not occur. The memory that is not reclaimed is said to have leaked. Over time, this can result in memory starvation.

It takes a seriously smart person to understand closures in javascript and why they cause memory leaks.  Personally I’m glad someone else has figured it out!  But it doesn’t mean we can’t protect ourselves and just do things the safe way.

That’s why using a good javascript library is such a no-brainer.  In prototype, you wire up events like this:

Event.observe(window, 'load', function() {//do something when the DOM is loaded});

This is a safe way of adding a load event to the page without erasing any previous load events that were wired up.  (Anyone ever had ASP.NET erase one of your button click handlers?  I have…)

$() extended elements also get this ability.  Say you have a button on the page like this:

<input type="button" id="activate" value="Activate the Reactor!" />

You can wire up a click even to this button by simply…

$('activate').observe('click', function() { alert('RUN!');});

Wiring up events in this manner will help keep you from leaking memory and generally provides better syntax.

Objects

Sometimes when you are writing code that has complex javascript, the functions seem to relate, however they are just placed in file in global scope.  This breaks down when you have lots of scripts from various components on a page and sooner or later things will start to conflict.  I mean, how many init() methods can you have on a page?  Would you write a C# program with nothing but 1 class?

Grouping this functionality into javascript classes can really help streamline and encapsulate your javascript code.  This is incredibly useful when you have a script for a control on a page but you need to support having multiple instances of that control on the same page.  On top of that, with ASP.NET you have to deal with the id munging, so things become even harder.

In prototype (as of 1.6), you create a class like this:

var Person = Class.create({initialize: function(name, ) {this.name = name;},greet: function() {alert("Hello, " + this.name + "!");}});//create a person instance like this:var joe = new Person("Joe");joe.greet(); // -->"Hello, Joe!"

The initialize() method is like a constructor.  It will get called when you “new” up your javascript class.  You can extend this object and add more functionality to it pretty easily.

Using Javascript Classes for your Components

Let’s say you want to create a type-ahead auto-complete textbox using ASP.NET.  Ignoring some of the canned-solutions for the sake of discussion, let’s also say we have to write it from scratch.  We’ll need some script to monitor the value of the textbox, and to send an ajax request with the current value, and to handle the response & fill in a list of values.

Here’s an example of how (a very crude) implementation might look:

 

//referenced (or rendered) only oncevar TypeAheadTextbox = Class.create({initialize: function(textbox_id, indicator_id, results_id) {this.textbox = $(textbox_id);this.indicator = $(indicator_id);this.results = $(results_id);//use .bindAsEventListener to preserve the 'this' pointerthis.textbox.observe('keyup', this.processKey.bindAsEventListener(this));},processKey: function() {//ignoring the interval, we'll just fire an ajax request for every lettervar value = $F(this.textbox);this.indicator.show();new Ajax.Request('/pirate_ships/find', {method: 'get',params: 'filter=' + value,onComplete: function() { this.indicator.hide(); },onFailure: function(e) { alert("Error with ajax request: " + e); },onSuccess: function(response) {this.results.innerHTML = response.responseText;}})}});//initializing multiple controlsEvent.observe(window, 'load', function(){var textbox1_autocomplete = new TypeAheadTextbox('<%= txtbox1.ClientID %>', '<%= indicator1.ClientID %>','<%= results1.ClientID %>');var textbox2_autocomplete = new TypeAheadTextbox('<%= txtbox2.ClientID %>', '<%= indicator2.ClientID %>','<%= results2.ClientID %>');});

Of course this code is incomplete, but it’s definitely a start, and it amazes me at how simple it is.  One hiccup that I ran across doing something similar was the .bindAsEventListener noise.  You can read about that here, but the short of it is that javascript will lose ‘this’ in those types of event handlers.  Using .bindAsEventListener will fix this for you.Another trick is to use something like:

initialize: function(msg) {var self = this; //for use laterthis.msg = msg;},foo: function() {alert(self.msg);}

We’ve also isolated the area where we need to pass in the generated ClientID of the controls we want to interact with.  This snippet of code can reside on the page, whereas the rest of the code reside ina separate file.

I’m no javascript ninja, but I have a vast appreciation for all that these libraries give us.  Yet it still amazes me that a lot of people don’t use them!  So go tell a friend or co-worker about the benefits of a good javascript libary.

#1 Eric avatar
Eric
7.07.2008
10:23 AM

Speaking of ninjas... This looks like an interesting book:Secrets of the Javascript Ninja by John Resighttp://www.manning.com/resig/Isn't Resig the guy who developed jQuery?


#2 Ben Scheirman avatar
Ben Scheirman
7.10.2008
12:13 PM

Yes you're right, he is.That book is on my reading list, but sadly the list is HUGE!


Leave a Comment