Storing Property Data In Javascript Function Objects
I was reading up on some jQuery plugin development the other day and I saw something that I had never seen before - someone was storing data as a property of a Javascript function. I have messed around with storing data in the Prototype of a function object, but I've never stored arbitrary data as a property; I didn't even know that one could do such a thing. If you have no idea what I'm talking about, here's a small example:
<script type="text/javascript">
// Define a simply method that alerts a property
// of itself.
function Test(){
alert( Test.Data );
}
// Define a property on the method.
Test.Data = "Method property";
// Execute the method.
Test();
</script>
Notice that we are defining the Test() function and then we are storing a property directly in that function as if the Function object were a standard object (which, in fact, it is). And, when we run this code, it properly alerts:
Method property
There's something really cool about this!
Now, I came across this when looking into jQuery plugin development and the way it was being used there was also very cool; it was being used to store a plugin's default options. It's common in jQuery to be able to pass an options-map into a function that can override a default option set. But, where is the default option set defined? In the example I saw, the default options were being stored as a property of function object itself:
<script type="text/javascript">
// Define the jQuery plugin.
jQuery.fn.Test = function( objOptions ){
// Use the passed-in option to override any default
// option that are provided by the plugin.
var objSettings = jQuery.extend(
{},
jQuery.fn.Test.Default,
objOptions
);
// Alert the final data value.
alert( objSettings.Data );
// Return this for method chaining.
return( this );
}
// Define the jQuery method's default properties.
jQuery.fn.Test.Default = {
Data: "Default data"
};
// When the document has loaded, run the plugin.
$(
function(){
$( document ).Test( {} );
}
);
</script>
Here, we define our plugin method, then after it, we define a default options map as a property directly on the function itself. Then, when the plugin is executed, we are extending this default property map with the passed-in property map.
How cool is that! I am not sure how I would use this outside of plugin development; but, for jQuery plugin development, this technique seems quite perfect.
Want to use code from this post? Check out the license.
Reader Comments
Hey, that's a really nice tip Ben. I saw it once, but didn't really bother to go after it and see what it was actually doing. Laziness at the extreme :-)
I've also been playing with some plug-in development in jQuery, and this sure will be useful.
The technique is used allow for a wide range of options to be passed into a function, typically as a configuration, without affecting an API's signature/contract. I first saw this in the Ext-JS library and it has been a constant throughout Ext's evolution.
It's also very powerful when combined with Crockford's Module pattern.
You call it laziness - I call it efficiency. ;)
@Marcos,
It's kind of odd to think you can store properties on a variable AND execute it. I guess if you think about parenthesis as operators, it's not so weird... but it's still weird :)
@Claude,
I'm definitely familiar with the idea of passing in an options object to a method for flexibility - jQuery does this on many things and it is certainly something that I've come to appreciate. What I thought was really cool, though, was how the default settings map was being stored as a property of the function itself - I had never seen this before.
Can you point me in the direction of Crockford's Module pattern. This sounds interesting.
You've seen it already in jQuery in the form of namespacing.
YUI Blog: http://yuiblog.com/blog/2007/06/12/module-pattern/
Chris Heilmann: http://www.wait-till-i.com/2007/07/24/show-love-to-the-module-pattern/
Not only can you do that but you can have private, privilaged and public members.
http://javascript.crockford.com/private.html
@Claude,
Thanks, I'll take a look at those links. I am a bit familiar with namespacing in jQuery, but am interested to learn more about how this is used.
@George,
I think the idea of private variables and methods is interesting, but I have not seen a real use for it just yet.
Another use of setting properties on a function is memoization. If you have a function that takes a while to run, after the first time it's called you can store or 'memoize' the result. Here's a crude example:
function fn() {
if (!fn.memo) fn.memo = expensiveOperation();
return fn.memo;
}
If you don't like having hardcoded references to the name of your function inside that function's definition, you can also use arguments.callee to get a reference:
function fn() {
var me = arguments.callee;
if (!me.memo) me.memo = expensiveOperation();
return me.memo;
}
This also allows you to get/set properties on anonymous functions!
I highly recommend Googling "javascript memoization" for a slew of terrific articles. Functional programming is an elegant paradigm, and it's making a comeback thanks to JavaScript.
@David,
Oh that is really cool :) I also totally forgot that you can get the callee from the argument collection. What's lame is that:
console.log( arguments )
... in FireBug does NOT output the callee property. No wonder I forgot about it (hmmph!).
Very cool tip though, thanks.
Callee is neat, but I think I've read that accessing callee is really slow in most interpreters. I believe that's also the case with accessing
Especially when you're talking about memoization -- which is explicitly about decreasing work (and hopefully improving performance) -- it can be ironically counter-productive.
If performance isn't an issue, then use of
and
can make for some really elegant code.
@Tom,
I have nothing to base this on, but I'd be very surprised to hear that "arguments" is actually a performance hit. I would assume that the runtime has to create it, no matter what, since it's part of the spec. Unless, it doesn't create it if it can see that it's never references.
@Ben,
I thought the same, but I wouldn't be surprised to learn that some interpreters cut corners for a speed boost -- I think browsers are still competing on JS performance, so there's probably some pressure.
Here's some original research (not mine) in the form of a JSPerf test: http://jsperf.com/arguments-performance
My read of this data is that accessing an input as "arguments[0]" is (for whatever reason) an order of magnitude slower than accessing it via the name provided by the function signature. This appears true for Chrome, Firefox, and Safari; IE9's performance is so poor there's no real difference among any of the 5 techniques tested, and apparently no one cares about Opera. So I would recommend against use of "arguments" except in cases where a function must accept an arbitrary number of args.
This isn't applicable to use of "callee" in any straightforward way -- and I'm not sure off the top of my head what suitable alternatives there are for performance testing -- but my gut tells me that these "meta" items are underused in the wild (or at least perceived as such by interpreter authors), and don't get a lot of love. That said, I'm not aware of any alternatives to "callee," and refusing to use it means discarding a whole bunch of cool patterns. I guess I'd just keep it out of stuff that really needs to sizzle.
@Tom,
Wow, that's super interesting. I have heard that different runtimes will compile down to "proprietary" classes if they can be sure certain objects are only used in a given way. So, clearly, they are cutting corners when they have the wiggle room. Anyway, very interesting!
One use for this would be to store named constants as properties of the function so rather than passing in just numbers or strings, you can pass in a named constant (which would be assigned to something like a number) similar to how FileError works.
I ended up using this in a weird way to make my code more readable. Maybe my code is just weird/wrong/horrific, or maybe mine is not a unique use case.
I created a factory function which collects all the objects it creates into an array. This collection is hidden in the 'private' scope of the immediately-invoked function which encapsulates what gets returned as a 'constructor'.
I could have given each instance a "getCollection()" method to access the array, but I think calling getCollection() on an instance looks like it gets something belonging to that instance, rather than the whole collection.
`
var myFactory = (function() {
var collection = [];
function myConstructor(someProp) {
self.xyz = someProp;
collection.push(self.xyz);
return self;
}
myConstructor.getCollection = function() {
return collection;
};
return myConstructor;
})();
var myObj = myFactory('a');
var myObj2 = myFactory('b');
alert(myFactory.getCollection());
`
@Phil,
Egads, sorry my code block syntax is wrong.