Skip to main content
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Kishore Balakrishnan
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Kishore Balakrishnan

The Power Of Closures - Deferred Object Bindings In jQuery 1.5

By
Published in Comments (10)

Over the weekend, I started to explore the Deferred objects that were added to jQuery 1.5. Deferred objects are stateful queues that can trigger success and fail event handlers. In a follow-up exploration, I wanted to see if I could use Deferred objects to power asynchronous script loading. As it turns out, you certainly can; but, the way in which I listened for the DOM-ready event should have raised some eyebrows.

Most of the asynchronous script loading that I wrote depends on the Promise objects returned from the $.ajax() method (which was completely rewritten in jQuery 1.5 to use Deferred objects). However, in addition to loading scripts, my asynchronous script loader also needed to wait for the DOM-ready event before it could use the loaded scripts.

In jQuery, we typically bind to the DOM-ready event by passing a function reference to the $() method:

$( function(){ ... DOM-ready code ... } );

Following this approach, I created a one-off Deferred object that would resolve when the DOM-ready event was fired:

$.Deferred(
	function( deferred ){

		// In addition to the script loading, we also
		// want to make sure that the DOM is ready to
		// be interacted with. As such, resolve a
		// deferred object using the $() function to
		// denote that the DOM is ready.
		$( deferred.resolve );

	}
)

As you can see, the logical end of this code is the following line:

$( deferred.resolve );

Here, rather than passing a lambda (anonymous) function to the jQuery constructor ($), as we might normally, we are passing a reference to the resolve() method of the one-off Deferred object instance. Passing function references can be a bit tricky, though; in this case, we're not actually passing the function as a method on the "deferred" instance - we're simply passing the free-floating function reference that happens to be a property of the deferred object.

The binding of a function doesn't matter until the function is invoked. This is why functions can be copied from one object to another; this is also why native functions like call() and apply() exist in Javascript and why functions like $.proxy() exist in jQuery.

So, how is it that we can pass the resolve() function reference and have it act upon the intended Deferred object? The answer: Closures. If you look at the source code for the jQuery 1.5 Deferred (and the _Deferred) constructor, you'll notice that it explicitly returns an object. This is why the "new" keyword is optional - you're not getting the instantiated Deferred class instance, you're getting an explicitly created object literal.

Without digging into the jQuery 1.5 source code, I can quickly illustrate this concept with a small demo. In the following code, I create a Girl constructor; but, within the constructor, notice that I am explicitly creating and returning a local object:

<!DOCTYPE html>
<html>
<head>
	<title>Deferred Object Bindings in jQuery 1.5</title>
	<script type="text/javascript" src="../jquery-1.5.js"></script>
	<script type="text/javascript">

		// Define the girl constructor. This returns a new Girl
		// instance, but not in the traditional sense.
		function Girl( name ){

			// Create a girl singleton.
			var girl = {

				// Set the name property.
				name: name,

				// I say hello to the calling person. Notice that
				// when this method invokes properties, it calls
				// them on the local "girl" instance. This function
				// has created a closure with the local context and
				// therefore has access to the "girl" instance no
				// matter how this method is invoked.
				sayHello: function(){
					return(
						"Hello, my name is " + girl.name + "."
					);
				}

			};

			// Return the girl instance. This will be different than
			// the actual instance created by the NEW constructor
			// called on the Girl class (though no references to the
			// NEW-based instance will be captured).
			return( girl );

		}


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// Create some girl instances.
		var sarah = new Girl( "Sarah" );
		var jilly = new Girl( "Jilly" );


		// Collect the sayHello methods. By collecting the method
		// references, they are no longer bound to their original
		// context objects; the closure behavior of the functions,
		// however, remains in-tact.
		var methods = [
			sarah.sayHello,
			jilly.sayHello
		];


		// Loop over the functions to execute them.
		$.each(
			methods,
			function( i, sayHello ){

				// Execute the context-less function.
				console.log( sayHello() );

			}
		);

	</script>
</head>
<body>
	<!-- Left intentionally blank. -->
</body>
</html>

Once I've defined my constructor, I create two instances of the Girl class which, in turn, creates two instances of the "girl" literal. Then, I am gathering up the two sayHello() method references and executing them outside the context of their original binding. When I do this, I get the following console output:

Hello, my name is Sarah.
Hello, my name is Jilly.

As you can see, the methods, though invoked without their parent context, are still bound to the appropriate "name" values. This is possible because we're not actually relying on the object binding; rather, we're relying on the method's lexical binding - in other words, its role in creating a closure.

Closures are not the easiest things to wrap your head around, so I've tried to outline this particular case graphically:

jQuery Deferred Objects Use Lexical Bindings To Make Sure Deferred Methods Always Reference The Right Deferred Instance.

Inside the Girl() constructor, our encapsulated "girl" object is defined within the local scope of the function. Then the sayHello() method is defined within constructor. As such, no matter how we pass the sayHello() function reference around, it will always have access to the "girl" object literal; the caveat to this, of course, being that the function cannot make use of the "this" keyword, which depends on the invocation context.

I find this to be particularly interesting because I've never actually returned anything other than "this" from a class constructor. To be honest, I don't think I was even aware that you could override the value returned from a class constructor; I assumed this was one of those things that was auto-wired by the language. Creating objects in this way adds overhead and method duplication; I have to assume that the jQuery team chose this approach specifically so that resolve() and reject() methods could be passed around without object binding. Very clever stuff!

Want to use code from this post? Check out the license.

Reader Comments

16 Comments

For the most part I've worked in languages having closures, but I often miss them when I work in Coldfusion. I've seen some clever work-arounds, but there's really no good substitute for closures the way that you can do them in languages like JavaScript, Perl, or Python.

This is a pretty good explanation of how to do closures and what their benefit is. Another good thing I've found, you can use this as a way of abstracting actions a little bit. Something like this...

var Dispatch = {
action1 : function (args) { return "action1";},
action2 : function (args) { return "action2";}
};

This is also something you can do with function references even in Coldfusion, but I really dig how things work with closures, and I like the ability to just code along, find a piece of logic where a function is needed for a small bit of logic, and then *BOOM!* Function.

15,848 Comments

@Themanchicken,

Yeah, closures would be cool in ColdFusion; but, you also need a language that plays well with them in general. ColdFusion would need to be re-thought a bit for closures to really make a good difference. Not that I'm advising against it - I think they would be awesome.

16 Comments

@Ben,
Absolutely! Though if I had one request for CF, it would be a simpler form of lexical scoping. With CF9 we see a nice move in this direction with the formal "local" scope, but that's still not completely lexical. Lexical scoping would be necessary in order to have closures I would think.

I think I come from a different perspective, I'm a guy who has spent ten years doing mostly back-end programming using a variety of Unix languages like Perl and Python, and I just miss some of those features.

CF9 makes a lot of big leaps, but we're still on CF8, and I don't really see us moving any time soon.

15,848 Comments

@Themanchicken,

Lexical binding is really the key to closures in general. I think people often refer to "anonymous functions" as being closures; this is true by coincidence. But, certainly, the true power of closures comes from the lexical binding.

16 Comments

@Ben,
I completely agree. That said, I like anonymous functions in and of themselves as well. I would also say that anonymous functions are an important part of closures, but they're not the most important part of an actual closure.

An anonymous function on its own is still useful though, especially in homoiconic languages and languages using prototype-based inheritance like JavaScript. It would make me smile if anonymous functions came to exist in Coldfusion, but closures would make me smile even wider.

16 Comments

@Ben,

It's nice to have someone agree with me on that. Some of my buddies are C programmers who assert that nothing more than function pointers are necessary. I maintain that once you've seen and wept over (okay, maybe that's a stretch) the beauty of anonymous subroutines AND closures, you really feel like a tool has been stolen from you when you work in environments without such features.

15,848 Comments

@Themanchicken,

Ha ha ha,

"nothing more than function pointers are necessary"

... Yeah, if you don't like "optimal" solutions :)

16 Comments

@Ben,

Completely! I mean, I love function pointers, don't get me wrong, but closures and anonymous functions are so awesome that Apple even managed to implement them in Objective-C for Cocoa-based platforms... and they are nice.

I understand it is possible to abuse and overuse features of a language, but without them in the toolbox we breed developers who don't know what tools are truly available. If someone has been developing for more than 10 years and has never been exposed to closures, they're really missing out on a pretty common and useful development technique.

It's sad really. Perhaps we should start a foundation for the closure-impaired. Maybe Sally Struthers will do the infomercial?

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel