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,314 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,314 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,314 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,314 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,314 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
Jun 18, 2013 at 9:20 PM
Mapping AngularJS Routes Onto URL Parameters And Client-Side Events
I couldn't find examples of passing multiple arguments using the when() routing statement so figured out through trial and error that you can pass multiple arguments using the following format: .whe ... read »
Jun 18, 2013 at 3:39 PM
Experimenting With The Amazon Simple Storage Service (S3) API Using ColdFusion
Hi Ben, THANKS! While not bleeding edge, it is new to me & I like learning new things every day! ... read »
Jun 18, 2013 at 12:30 PM
Disabling Auto-Correct And Auto-Capitalize Features On iPhone Inputs
Also spellcheck="false" should be mentioned as part of html5 specs ... read »
Jun 18, 2013 at 8:40 AM
Using Named Functions Within Self-Executing Function Blocks In Javascript
Hi Ben, you forgot to mention the most important thing for named self-executing functions - they can be referenced by name ONLY inside their execution context (which is parens in this case), it mean ... read »
dee
Jun 18, 2013 at 7:01 AM
My Safari Browser SQLite Database Hello World Example
hai ben, this program is really good i could understand the concept but i dint know how to save it and how to open it as you have done in the video can u give that details pls ... read »
Jun 18, 2013 at 6:04 AM
Clearing Inline CSS Properties With jQuery
Thanks a lot for for post! It helped me a lot... after being stuck since 24 hrs.. found solution from your post. Thanks again! ... read »
Jun 18, 2013 at 2:31 AM
SOTR 2013 - The Best Conference I Never Went To
I keep watching it, should keep me happily distracted until SotR14 ;) ... read »
Jun 17, 2013 at 9:45 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, As I was reading what you wrote, it occurred to me that maybe I do something similar to that in some of my client-side code. In an application I'm working on, there are a bunch of unrelated ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools