Storing Property Data In Javascript Function Objects

Posted March 10, 2009 at 9:34 AM by Ben Nadel

Tags: Javascript / DHTML

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.



Reader Comments

Mar 10, 2009 at 9:51 AM // reply »
14 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.


Mar 10, 2009 at 9:54 AM // reply »
9 Comments

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.


Mar 10, 2009 at 9:55 AM // reply »
319 Comments

You call it laziness - I call it efficiency. ;)


Mar 10, 2009 at 9:58 AM // reply »
11,243 Comments

@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 :)


Mar 10, 2009 at 10:02 AM // reply »
11,243 Comments

@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.


Mar 10, 2009 at 10:16 AM // reply »
9 Comments

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/


Mar 10, 2009 at 11:01 AM // reply »
33 Comments

Not only can you do that but you can have private, privilaged and public members.

http://javascript.crockford.com/private.html


Mar 10, 2009 at 11:07 AM // reply »
11,243 Comments

@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.


Mar 10, 2009 at 2:15 PM // reply »
29 Comments

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.


Mar 11, 2009 at 8:31 AM // reply »
11,243 Comments

@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.


Mar 29, 2013 at 2:27 PM // reply »
2 Comments

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

  • arguments

. 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

  • arguments

and

  • callee

can make for some really elegant code.


Apr 4, 2013 at 9:38 AM // reply »
11,243 Comments

@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.


Apr 4, 2013 at 1:42 PM // reply »
2 Comments

@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.


Apr 5, 2013 at 9:41 AM // reply »
11,243 Comments

@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!



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
May 23, 2013 at 5:19 AM
Ask Ben: Print Part Of A Web Page With jQuery
How to print also the background color of table cells and table lines ... read »
May 23, 2013 at 3:55 AM
Javascript Array Methods: Unshift(), Shift(), Push(), And Pop()
very interesting and helpful too. ... read »
May 22, 2013 at 5:35 PM
Script Tags, jQuery, And Html(), Text() And Contents()
This is still an issue 2 years later. jQuery is supposed to remediate these cross browser issues, no? I have been unable to find any statement from the jQuery team calling this behavior "by de ... read »
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools