Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Sebastian Zartner
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Sebastian Zartner

Creating Divergent Javascript Class Methods With Self-Executing Functions

By
Published in Comments (13)

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.

Want to use code from this post? Check out the license.

Reader Comments

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 ;)

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

15,902 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 :)

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.

15,902 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.

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.

15,902 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.

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.

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

15,902 Comments

@Drew,

No worries my man.

@Dan,

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

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.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel