Lexical Closures are Handy

Posted in Development by MB on 07/20/08

I wrote previously that I would write about a handy trick in JavaScript known as a “lexical closure”.

This article has a great definition of a lexical closure: http://www.brockman.se/writing/method-references.html.utf8.

I quote…

Essentially, a closure, or lexical closure, is a function f coupled with a snapshot of its lexical environment (i.e., the non-local variable bindings used in its body).

Hence, closing over some variable v means creating a closure that refers to v.

Strictly, the term “closure” can be used to describe any function that refers to one or more variables in an outer lexical scope. This is a rather broad definition that includes, for example, all functions that refer to global variables (such as document, alert, String, and so on).

This can be very handy if you want, say, an event handling method to interact with both the HTML element the handler is attached to, and the parent object to which the method belongs. Huh? Exactly.

Let’s say you want to write a JavaScript object which encapsulates all of your form validation and preprocessing logic. Let’s say you want this object definition to include event handlers which validate form elements. After validation, you want the form elements to be visually updated to indicate if the value is correctly entered or not, and update the JS Object with any validation error messages.

First let’s write up some simple event handling utilities.

// creates a function which
// 		executes an event handling method
// 		in the scope of a given object
function getBoundEventHandler(obj,method)
{
	return function(e)
	{
		e = e || window.event;
		method.call(obj,e);
	}
}

// cross-browser event-adder-on
function observeEvent(strEvent,obj,handle)
{
	if (window.addEventListener)
	{
		// W3C
		obj.addEventListener(strEvent,handle,false);
	}
	else
	{
		// Dirty, dirty MS Internet Explorer
		obj.attachEvent("on"+strEvent,handle);
	}
}

Now let’s define a simple form handling object, with a simple means to report errors, and validate an alpha-numeric field.

First the constructor function:

function Formbois(strFormId)
{
	var that = this; // Hot Lexical Closure Action!!
	var this.errors = new Array();
	var this.fieldKeyHandler = function(e)
	{
		// with the handler function bound to the target element,
		//   "this" refers to the form element
		// "that" refers to the Formbois instance
		if (!that.validateAlphaNum(this.value))
		{
			// add an error to the errors array
			that.errors.push("Please enter an alpha-numeric value");
			// mark the form element having an error
			this.className += " error";
		}
		else
		{
			this.className += " valid";
		}

	}
}

Based upon what I mentioned in my previous post, the private variable “that” is in the same lexical scope as the function “fieldKeyHandler”. That means when I assign that method to a form element, the variable “that” will refer to the instance of the object “Formbois” allowing me to access the public method “validateAlphaNum” and the “errors” array. Neat.

Let’s define the validator and the error reporter for a complete example:

Formbois.prototype.validateAlphaNum = function(value)
{
	return value.match(/[a-z][A-z][0-9]/))

}

Formbois.prototype.alertErrors = function()
{
	alert(this.errors.join("\n"));
}

Neither of these methods have access to the private variable “that”, but that’s OK because they don’t need it. The method “validateAlphaNum” could even be a static function assigned like so:
Formbois.validateAlphaNum = function(value) { /*[.code goes here.]*/ }

To put it all together, we instantiate the “Formbois” object and assign our event handler.

// create form object
var f = new Formbois("happyFunForm");
// get form element
var elm = document.getElementById("someTextFieldId");
// assign event handler to "onkeyup" event
observeEvent(
		"keyup",
		elm,
		getBoundEventHandler(elm,f.fieldKeyHandler)
	);

Obviously this is a very simple example, but hopefully it will inspire someone to write some JavaScript which doesn’t suck.

« Previous | Next »


Leave a comment.
Trackback.