Creating Divergent Javascript Class Methods With Self-Executing Functions

Posted November 29, 2010 at 10:08 AM by Ben Nadel

Tags: Javascript / DHTML

A while back, I blogged about overloading Javascript function signatures using a sub-function approach. In that blog entry, I advocated factoring out divergent functionality into separate functions that were ultimately members of the primary function. In that approach, all three functions were publicly available; after reading Stoyan Stefanov's Javascript Patterns, however, I realized that this kind of branching logic could be applied to Javascript class methods using self-executing functions (or more technically accurate, Immediately-Invoked Function Expressions).

In the past, I've only ever used self-executing functions as stand-alone constructs. In Javascript Patterns, however, Stoyan Stefanov demonstrated that these self-executing functions could be used to define Javascript class methods:

  • Class.method = (function(){ ... return( function ) ... })();

Here, the function expression is being invoked immediately after it is defined. Then, as part of that initial execution, the function expression returns yet another function expression which will ultimately become the class method reference.

With this new context for self-executing functions, I wanted to re-visit my function overloading logic for use in defining class methods. In the following simple example, I have created a Woman class that has only one method: sayHello(). The divergent logic within the sayHello() method is handled by the actual class member; however, the eventual execution is handed off to functions that have been scoped to the local self-execution context.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Creating Divergent Javascript Class Members With Self-Executing Functions</title>
  • <script type="text/javascript" src="./jquery-1.4.3.js"></script>
  • <script type="text/javascript">
  •  
  • // I am the Woman class constructor.
  • function Woman( name ){
  • this.name = name;
  •  
  • // Give this woman a random mood 0:<1. This will
  • // influence the way she responds to greetings.
  • this.mood = Math.random();
  • }
  •  
  •  
  • // Define a class method for greeting people. The way in
  • // which people are greeted will be depended on this
  • // woman's mood at the time of class instantiation.
  • Woman.prototype.sayHello = (function(){
  •  
  • // I say hello in a nice manner.
  • var sayHelloNicely = function( toName ){
  • return( "Hey " + toName + ", I'm " + this.name + "." );
  • };
  •  
  • // I say hello with attitude.
  • var sayHelloWithAttitude = function( toName ){
  • return( "What do you want, " + toName + "?" );
  • };
  •  
  • // Return the actual class method that will defer
  • // execution off to one of the hidden functions. Notice
  • // that the actual class member only contins the
  • // branching logic while the core execution is deferred
  • // to the private members in this immediately executing
  • // function.
  • return(function( toName ){
  •  
  • // Check to see what kind of static mood we have.
  • if (this.mood < .7){
  •  
  • // I am in a bad mood - be mean.
  • return( sayHelloWithAttitude.call( this, toName ) );
  •  
  • } else {
  •  
  • // I am in a good mood - be nice :)
  • return( sayHelloNicely.call( this, toName ) );
  •  
  • }
  •  
  • });
  •  
  • })();
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Create a few women. These women will all have random
  • // moods and will respond accordingly when greeted.
  • var women = [
  • new Woman( "Sarah" ),
  • new Woman( "Tricia" ),
  • new Woman( "Katie" ),
  • new Woman( "Amanda" ),
  • new Woman( "Joanna" )
  • ];
  •  
  • // Now, say hi to each one.
  • $.each(
  • women,
  • function( index, woman ){
  •  
  • console.log(
  • woman.sayHello( "Ben" )
  • );
  •  
  • }
  • );
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Intentionally left blank. -->
  • </body>
  • </html>

As you can see, we are defining the sayHello() method using a self-executing function. Within the self-executing context, I am creating two, locally scoped functions: sayHelloNicely() and sayHelloWithAttitude(). These two functions are not members of the Woman class - they are not available to any objects outside of the self-executing function. As such, the returned function expression (that which becomes the sayHello() class method) is the only construct that can make use of them.

When we run the above code, we get the following console output:

What do you want, Ben?
Hey Ben, I'm Tricia.
Hey Ben, I'm Katie.
What do you want, Ben?
Hey Ben, I'm Joanna.

As you can see, all five women were given the sayHello() message; however, only three of them responded kindly (yeah, I wish I had that kind of success!).

I know that this example doesn't actually overload a method signature (as I demonstrated in my first blog post); however, it does demonstrate a class method with branched execution logic. Of course, branching can always be done inline; but, I think it creates more readable, more maintainable code when branching can actually be broken up into smaller functions that have a narrower set of responsibilities.




Reader Comments

Nov 29, 2010 at 11:39 AM // reply »
1 Comments

This is a great example of how to use the Strategy and Factory patterns in Javascript to give an object different behavior. I love the way you implemented the inner functions to make how the woman will respond a complete mystery to anything outside the context. Just like real life ;)


Nov 29, 2010 at 11:55 AM // reply »
171 Comments

I noticed you like to use the call() method in your code, but I recommend using the apply() method instead. It's similar to the call() method, but takes an array-like object as the second argument--which is way easier to implement w/dynamic code.

So, instead of:

sayHelloWithAttitude.call( this, toName )

You'd could use:

sayHelloWithAttitude.apply( this, arguments );

That way if you add additional arguments, you don't have to worry about updating the calls to the sayHelloWith* functions.


Nov 29, 2010 at 2:18 PM // reply »
11,246 Comments

@Steve,

Ha ha ha :) That made me laugh out loud.

@Dan,

I actually went back and forth on that in this example. At first, I had the apply() method since, as you say, it's just easier to pass around the arguments collection. But then, I started to feel bad that my example didn't actually deal with method overloading (which is kind of where I started the post). As such, I reverted back to call(), where theoretically, you could pass a different number of arguments to each inner-method implementation.

Of course, you could still do the same with the arguments scope.

All to say, I typically prefer apply(). But at 9am, something about call() felt more inviting :)


Nov 29, 2010 at 5:31 PM // reply »
54 Comments

JavaScript doesn't have classes!!!!

John Resig has a well-abstracted method for creating method overloading based on number of arguments. http://ejohn.org/blog/javascript-method-overloading/

This is really weird... do you have more info about what happens when you use a self-executing function as a prototype method? Obviously, every object that is created reruns this so you have really unexpected behavior from a prototype method. Normally, you think that this method is shared by all but you are effectively rewriting the prototype method on every instantiation.


Nov 29, 2010 at 5:42 PM // reply »
11,246 Comments

@Drew,

Ha ha, if I can call "new" on something, my brain says "Class" :D I know, I know, they're not classes in the traditional sense; but, you are defining an object and then creating an instance of it... sort of... and then mix in prototype chains and bindings.

Awesome link! I had not seen that one before and I had no idea that you could check the number of arguments on a Javascript method. I had seen "arity" mentioned a few times; but, quickly looking at the Mozilla Dev Docs, it looks like arity is being deprecated in lieu of length.

As far as the self-execution method and instantiation, the self-execution method only runs once - when you are defining the prototype. Once it has run, the resultant function is what gets placed in the object prototype. Remember, each instance then shares the same prototype and all have access to the resultant function.


Nov 29, 2010 at 5:46 PM // reply »
54 Comments

Well if it were only executed once, wouldn't they all have the same 'mood'. I can see what you mean here, but usually you think the prototype is shared between all instances of Woman here it is not the case, some are moodier than others :p.

arity is the classical definition of what length is doing here, it has been deprecated. Also, I believe length will be deprecated (or removed) from HTML5 strict mode.


Nov 29, 2010 at 5:50 PM // reply »
11,246 Comments

@Drew,

When each new Woman object is instantiated, each is given a random mood as part of the object constructor. Yes, they all share the same sayHello() method; however, that method, internally, refers to the instance-specific "this.mood", which each Woman has independently.


Nov 29, 2010 at 7:00 PM // reply »
54 Comments

@Ben,
Oh yeah, I read the code wrong. You're actually evaluating this.mood every time the method is called. I saw something totally wacko going on not sure why. Thanks for the clarification.


Nov 30, 2010 at 8:20 AM // reply »
171 Comments

@Ben:

You don't have to pass the argument object in apply, you can pass in any array object so you could have just as easily written:

sayHelloWithAttitude.apply(this, [toName])

However, it's often very convinent to pass in an arguments object if you're just rediecting a call to the current function.


Dec 5, 2010 at 2:49 PM // reply »
11,246 Comments

@Drew,

No worries my man.

@Dan,

Excellent point; I don't often think about passing implicitly created arrays as the "arguments" collection.


Dec 31, 2010 at 1:23 PM // reply »
1 Comments

Thanks for this useful post, although I do wish you didn't have to use such a sexist example. I'm a female developer, but I never feel my code examples need to highlight men's football watching habits, beer drinking etc.

For some education, I highly encourage you to review what happened in the rails community following an extreme example of what can happen when sexist remarks aren't discouraged in the community: http://geekfeminism.wikia.com/wiki/CouchDB_talk

I appreciate your time and hope you give this post some thought.


Jan 6, 2011 at 9:26 AM // reply »
11,246 Comments

@Irene,

I can understand if you think my example might lack taste; but, I would hardly call it sexist.


Jan 12, 2011 at 1:37 AM // reply »
1 Comments

Great article

Thanks for the information



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 25, 2013 at 10:08 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Ben, my question is that i want the current node with its tag and its parent node. i just want only that data. So, give me the solution for that. and remember solution is working on " xpath 1.0 ... read »
May 25, 2013 at 10:01 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
hey ben, i want get my current node tag and also want the root node tag withing. So, how can i fix it.. ! ... read »
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools