Tuesday, December 8, 2009

jQuery Namespacing / Child Plugins / Modularization

When writing jQuery plugins, you'll find that you want to group related functionality into a single "namespace" or "module", that acts as a parent for multiple child plugins.

There is not a lot of information readily accessible about the subject, from what I can tell.  So little in fact that only after I came up with a quick and dirty method for myself did I dive deep enough to find anything.

Malnota's Wordpress Blog shows examples of three plugins that aim to do the job.  They aren't reviews so much as they are demonstrations.

First up is jQuery.NameSpace by Gilberto Saraiva.  Gilberto posts an example along with his download link, but to be honest with you, I don't follow any of it.  Could be the moving stuff in the background of the blog.  Could be the hour that I'm writing this at (I really should be in bed, kids!).

Malnota writes up the features a little more clearly in his review/article but again, I don't get a sense of what it's doing under the hood or how well it performs.  Except that Malnota says that the "child" plugin did not receive arguments that were passed to it.


A relatively casual review of the code shows that Saraiva is going through a lot of complicated eval gymnastics, and hasn't commented a single thing that he's doing.  Double UH-OH.  Seriously, though this looks like it took a lot of effort to complete and using my spidey sense, it might be close to a neat solution to the problem.  I'll tell you that I like the fact that you don't need to call the parent/namespace as a function in order to use it.  If nothing else, the approach taken here is interesting because of that aspect.

Next we have a solution created by the master himself, John Resig.  Is this a good time to mention that my friends used to call ME ejohn?  I wonder if I could have snapped up the domain name before he got to it and somehow switched places with him like he was my Dad and it was Vice Versa.  That's right, I too wish that I could be John Resig.

John's solution is jQuery.space and the thing to say about it is that he has his own ideas about how to approach the situation.  From the way that I read the code.. he's doing the same thing that Gilberto does in jQuery.NameSpace, but in a much more elegant fashion.  It looks to me like he's creating a new copy of jQuery.fn for each namespace.  Then when you want to stop working that space, you do the magical endSpace call and this code runs:

newjQuery.endSpace = function(){
  newjQuery.fn = oldFn;

Just like magic you climb out of the rabbit hole. Hey, that's pretty neat.  That's why they call him "The King".

Last out of the gate is jQuery.Modularize by Arial Flesler.  I analyzed this one more than the other two to figure out what it was doing.  Essentially, it creates an inner copy of the namespace, and stores the "this" (jQuery object) that is normally expected there and currying each function so that it will be called with the stored "this".

If you have a namespace "foo" (called via jQuery.foo() natch!) and methods "bar" and "baz", jQuery.Modularize creates "foo._" and "foo._.bar" and "foo._.baz" and "foo._._s_" (this).  It only does the magic currying part of the algorithm once, or when you specify "lazy: true".

I find the cryptic ._. and _s_ and _o_ stuff to be offputting when studying the source code, but overall this is quite the nifty piece of craftsmanship.  I was going to complain about the storing of _s_ (data) with the namespace (code) but then I remembered that javascript doesn't have threads so there's no way that it can collide with a simultaneous call for the same namespace with different data.

What a world.

If you've made it this far and want to see what I did, have a peek at the madscience namespace plugin in my github.

My approach is really freaking simple.  I was happy I came up with anything.  Since anything I write isn't likely to have 10,000 child plugins, I think that simplicity and terseness might make up for the lack of wowee zowee shown.  Certainly I'll be leaving it as is for the purposes it was written ...

And I'll probably add links to this blog post and/or the two top namespacing plugins reviewed into the comments.  So that the people who see my code can go see that there are other, more subtle ways to solve this problem.

P.S. John's plugin isn't up at plugins.jquery.com ... and unfortunately that site is a little bit app-store-licious ... I rarely find anything good searching it.

UPDATE: I think that there could be a collision in the stored jQuery object _s_ in the jQuery.Modularize plugin if you were using the same namespace in multiple asynchronous callbacks/timers.  Something like this:

jQuery("#content").foo().bar(function () { jQuery("#heading").foo().bar(); this.baz(); });

If I am reading the control flow correctly, this.baz() will act on "#heading" and not "#content".  I'll ping Flesler and see whether this has been considered or is a real life (edge) case or not.

No comments:

Post a Comment