$$: Cache, or Crash
I recently revised my opinion of a little JavaScript framework you may have heard of. Frankly, I just got tired of simulating inheritance on my own, and Prototype does a better-than-decent job of class extensions, which is more than I can say for other libraries like JQuery or MochiKit.
However, that being said, it’s also worth noting the fact that Sam Stephenson – creator of the Prototype framework – is also a lead developer on RoR, which means that no matter what framework he’s building or what language it’s in, he’s going to make sure arbitrary – borderline useless – methods and helper functions plague the code base. Among these are things like Enumerable.zip, Element.makeClipping, and the oh-so-overrated Rails “dollar-w” Array method, $w. Now, one could argue that absolutely every inch of Prototype – or any framework, for that matter – simply consists of convenience methods and shorthand coding patterns – and that may be true. I mean, let’s face it, a framework can only utiliize a languages already-built-in capabilities – it can’t add to them. Regardless, I have become rather fond of using Prototype’s XPath parsing engine.
You may recognize some Prototype functions that utilize the XPath DOM traversal method like $$, Element.getElementsByClassName and
Element.getElementsBySelector. However, these are H-E-A-V-Y on your browser. How heavy? YMMV, but generally, pretty fucking heavy. So heavy, in fact, that Google decided this was a good idea. The question is, do I care? Meh, nah, not so much. I increase browser performance like Chris Brown punches out pop starlets.
The following is a generic caching object I tend to use rather frequently:
1 2 3 4 5 6 7 8 9 10 11 | var Cache = function () { this._cache = {}; }; Cache.prototype.retrieve = function ( key ) { return ( key in this._cache ) ? this._cache[ key ] : null; } Cache.prototype.store = function ( key, value ) { this._cache[ key ] = value; } |
If I had a nickel every time my little Cache class got me out of a bind – well, let’s just say I’d have 15 cents, if that’s any indication. Consider the following:
12 13 14 15 16 17 18 19 20 21 22 | var xPathCache = new Cache, $$_proxied = $$; $$ = function () { var pattern = $A( arguments ).join( "||" ); if ( xPathCache.retrieve( pattern ) == null ) xPathCache.store( pattern, $$_proxied.apply( $$_proxied, arguments ) ); return xPathCache.retrieve(pattern); } |
What did we just do? Essentially, we have proxied Prototype’s $$ Utility method with our own function that first checks our global xPathCache caching object, just to see if the XPath pattern has already been searched for once. If it has, it simply returns the stored result set from the browsers temporary memory. If not, it executes the dollar-dollar call, storing the result set for future reference – yep, you got it, exactly like SQL query caching (only efficient). Wasn’t that fun?
“But John! What about memory leaks in IE and potentially running out of storage in the browser’s channel! Wah! My ‘wah’ hurts!” (courtesy: Saenz)
Well, actually, this method decreases memory leaks, especially in IE6 and below, by assigning references to each instance of $$. Memory leaking can occur in a variety of ways, but one of the more prevalent ways is when the browser decides NOT to create reference pointers to newly allocated memory, which is typical of IE and Microsoft products in general. The newly-allocated memory becomes a lost child in a super-Walmart – but don’t expect the doors to be sealed until it’s found. It then stagnates, and ultimately, the browser crashes. By using the caching object, every single call to $$ – and more specifically, the returned result set – is stored in a global variable, ready for garbage-collection roll-call.
This is a fairly efficient way to use $$, guilt-free. This method of caching can also be easily modified to support other XPath methods, such as Element.getElementsBySelector. Just don’t expect this to, in any way, speed up certain browser’s initial $$ call – as the great philosopher Mick Jagger once wrote:
“You can’t always get what you want.”
You did, however, get a ready-to-use caching method for what is arguably Prototype’s most memory-intensive function, so it’s not like you’re completely deprived.
November 18th, 2009 at 11:14 am
Hah! Funny. I like you’re writing style; great post. I work with Prototype extensively, and I’m definitely implementing this on my next project.
Great article!