Mr Speaker

Using jQuery on JavaScript objects (Part 1)

Today I was trying to decouple some JavaScript classes in a game prototype I've been working on. I didn't want to get into implementing some kind of interface behaviour, and so I thought about a simple Observer pattern. There are a bunch of solutions around the tubes (and indeed I'll probably use one of them) but I started wishing I could just use jQuery's custom events directly on my JavaScript classes.

This idea seemed pretty wacky to me, and made me smile: jQuery events attach themselves to DOM elements - wrapping a JavaScript object in the jQuery selector seemed ludicrous - I mean, should I be able to do $(class).hide()?! No, of course not. But none the less, my curiosity got the better of me, and I tried this lil' snippet:

function cPerson( name, age ){
	this.name = name;
	this.age = age;

	$( this ).bind( "yell", function(){
		alert( this.name + " is " + this.age + " years old!!" );
	})
}
var person1 = new cPerson( "Bob", 32 );
$( person1 ).trigger( "yell" );

To my astonishment, it worked! The custom event fired! I quickly delved into the jQuery source to find out how it handled the object. Skipping through all the regular selector handlers, our object gets converted into an array like so:

return this.setArray(jQuery.isArray( selector ) ?
			selector :
			jQuery.makeArray(selector));

Which is also how other objects like "document" and "window" get handled. Ahh yeah, document and window... it's not that ludicrous after all. I tested it on functions which, I reasoned, should also work - but alas there is a path in the jQuery selector code that says:

if ( jQuery.isFunction( selector ) )
	return jQuery( document ).ready( selector )

So functions fall through to the document.ready shortcut code. Ah well.

But wait, what else can we do with a wrapped instance of an object? Obviously "slideDown()" is a bit stupid? And clone(), appendTo(), wrapAll() etc. don't make sense... how abooouut... data? or plugins?!

$.fn.misterise = function(){
  	return this.each(function() {
 		this.name = "Mr " + this.name;
		this.name += $(this).data( "surname" ) || "";
	});
};

var person1 = new cPerson( "Bob", 32 );
var person2 = new cPerson( "Dave", 27 );
var peeps = [ person1, person2 ];

$( peeps )
	.data( "surname", " Dobalina" )
	.misterise()
	.trigger( "yell" );

And out pops Mr Bob Dobalina & Mr Dave Dobalina! I'm not sure of any practical purpose for this odd bit of functionality. Perhaps some kind of AOP meta-data system or, um, no... I dunno. Here's a plugin for executing methods on an object by name:


$.fn.go = function( funcName ){
	return this.each(function(){
		if( $.isFunction( this[ funcName ] ) ){
			this[ funcName ]();
		}
	});
};

That's pretty stupid and cool. Using a plugin against your JavaScript classes instead of a simple method call will most likely get you killed by your colleagues, but hey, it keeps things looking jQuery-ish. And that's the important thing.

Update: Futher investigation has lead to some interesting discoveries... Go have a read!

2 Comments

  1. Very useful! I didn’t know this.
    I definitely will use the “go” function in my upcoming projects, I just added the “arg” parameter to handle over parameters similar to the standard “apply” function.

    Have Fun !
    Rusco

    $.fn.go = function( funcName, args ){
    return this.each(function(){
    if( $.isFunction( this[ funcName ] ) ){
    this[ funcName ](args);}
    });
    };

    Saturday, November 7, 2009 at 12:30 am | Permalink
  2. Nice work Rusco! I’m scared to see where this might possibly lead ;)

    Saturday, November 14, 2009 at 11:15 am | Permalink
Captcha! Please type 'radical' here: *
How did you find this thingo? *