Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript

Posted October 3, 2011 at 10:08 AM by Ben Nadel

Tags: Javascript / DHTML

Over the weekend, I attended the jQuery Conference 2011 up in Boston. In the CoffeeScript presentation by Mark Bates, I saw something very interesting. When he showed us how the CoffeeScript was compiled down into JavaScript, I saw the conversion using an approach that I had never seen before: the compiler was explicitly setting the context of self-executing function blocks.

Self-executing function blocks (or IIFEs: immediately-invoked function expressions) are not methods; that is, they are not called as properties of an object. As such, they execute in the context of the global scope. This means that within the code of a self-execution function block, the "this" keyword is [generally speaking] a reference to the global scope which, in the browser, is the window object.

In JavaScript, every Function instance has two methods that can be used to change the context of its execution for a particular invocation. These methods are call() and apply(). While they each have a different method signature, both take an explicit context as their first argument. While a self-executing function block is somewhat unusual, it's still a Function instance and can, therefore, still make use of call() and apply().

To see this in action, let's take a look at the following demo. In this scenario, we are using call() to explicitly define the "this" context of the self-executing function block:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Changing The Context of a Self-Executing Function</title>
  •  
  • <script type="text/javascript">
  •  
  •  
  • // Set the singleton value to the return value of the self-
  • // executing function block.
  • var singleton = (function(){
  •  
  •  
  • // Declare a private variable.
  • var message = "Stop playing with your context!";
  •  
  •  
  • // Declare a public method. In this function, the "THIS"
  • // keyword is a reference to the object literal context
  • // defined with the .call() method.
  • this.getMessage = function(){
  •  
  • return( message );
  •  
  • };
  •  
  •  
  • // Return this object reference.
  • return( this );
  •  
  •  
  • }).call( {} );
  • // NOTE: When we are invoking the self-executing function
  • // block, we are using CALL() to change the execution
  • // context of the function. In this case, we are setting it
  • // equal to the object literal, {}.
  •  
  •  
  • // Log the singleton message.
  • console.log( "Message:", singleton.getMessage() );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

As you can see, our self-execution function is taking the general format:

  • ( function(){ .... } ).call( newContext );

The call() method is being used to dynamically bind the execution context of the function block to the "newContext" object. In our case, the new context is an empty object literal. By doing this, it allows us to define our public methods using the "this" scope, which in my opinion, is much more readable that defining them in some massive return() statement.

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

Message: Stop playing with your context!

As you can see, "this.getMessage" was bound to the object literal context which made it a public method of the resultant singleton reference.

While I like the module pattern, I have never been a fan of defining public methods in some bloated return() statement at the end of my code. I'll be very interested to see if this dynamic context binding will make self-executing function blocks easier to define and to read. My gut is telling me yes; but, only experience will truly answer this question.




Reader Comments

Oct 3, 2011 at 10:22 AM // reply »
33 Comments

This is how I do most all my JS these days (pretty close anyway). Essentially this allows objects on "this" to be "public", and "var" scoped objects to be "private".

Then, if I need a singleton I invoke like you show above. Otherwise for a "class" I do not, allowing instantiation.

Cool stuff Ben.


Oct 3, 2011 at 10:29 AM // reply »
11,243 Comments

@Adam,

I'm glad that you're finding this approach to be useful. When I saw it yesterday in the slides, it really jumped out at me. This is the first time I've played with it, but like I said, my gut is telling me that it's gonna be sweeeeeet :)


Oct 3, 2011 at 10:48 AM // reply »
10 Comments

This is awesome. I can't believe I haven't used it before, it looks so obvious! Updating code..


Oct 3, 2011 at 10:52 AM // reply »
11,243 Comments

@Wilkins,

Ha ha, awesome. Yeah, I had never seen this use-case before either. I'm excited about it.


Bob
Oct 3, 2011 at 12:40 PM // reply »
3 Comments

If you're going to do .call on it, you don't really need the extra parentheses, do you? What I mean is, instead of this:

( function(){ .... } ).call( newContext );

Just this:

function(){ .... }.call( newContext );

Doing that with your sample code produces the same result.


Oct 3, 2011 at 12:43 PM // reply »
11,243 Comments

@Bob,

That could be true. I am a fan of syntax though. Color me happy when I can add semi-colons and parenthesis :D


Oct 3, 2011 at 1:05 PM // reply »
369 Comments

@Ben, I love parenthesis...to me, they just seem to help me organize things in my head so much better.


Oct 4, 2011 at 4:58 PM // reply »
6 Comments

@Bob,

In the above case the code still works without surrounding the function in parens, but if you weren't saving the result to a variable the JavaScript engine would not be able to parse it.

  • function() {}.call( yourContext ); //Error
  • var result = function() {}.call( yourContext ); //Works

whereas

  • (function() {}.call( yourContext ); //Works
  • var result = (function() {}).call( yourContext ); //Works

You'll typically see the second option primarily because it works in either case and it tells the reader that something special is going on.

This technique is similar to the IIFE that you often see where you pass in your content and add to it (instead of using this);

  • var result = (function( context ) { context.hello = function() {}; })( yourContext ); //Works

The reason for the extra parens is similar to the above reason. Check out Ben Alman's article for much more explanation... http://benalman.com/news/2010/11/immediately-invoked-function-expression/


Feb 3, 2012 at 2:04 PM // reply »
1 Comments

To quote Jurassic Park: "Just because you can doesn't mean you should".

I completely, utterly disagree with the thought that this is more readable. Consider the current module pattern: if you want to see the public API, just scan to the bottom and they are spelled our explicitly for you. This is neat, clean and 100% self documenting

return {
fnSetCfg : fnSetCfg,
fnMakeInfoBox : fnMakeInfoBox,
fnEach : fnEach,
fnCloseAll : fnCloseAll,
fnHideAll : fnHideAll,
fnShowAll : fnShowAll,
fnRemoveAll : fnRemoveAll
};
}());

Now my former coworker would instead sprinkle public and private method declarations all over in the code, and it was a complete, serious PITA to go spelunking to find where capabilities were coming from in the prototype chain.

Cool? Yes. A good idea? Probably not, IMO.


Feb 3, 2012 at 2:28 PM // reply »
11,243 Comments

@Michael,

You definitely make a good point (and extra points for quoting movies - I love movies). When you use a return() statement to define the object's public API, it does provide a consistent and quickly findable place for the method list.

On one hand, I *want* to say that you should have to look at source code to figure out what an object does - that it should be documented somewhere (or at least commented at the top of the JavaScript module file).

The *reality* of the situation is, however, that most code is NOT documented; and when it comes to code you write on a team, the chances are probably even smaller!

Ultimately, when you work on a team, you probably need to come up with something that everyone agrees upon. Being able to coordinate and maintain code is definitely the large cost over time - more important than design choice philosophies probably.


Apr 17, 2012 at 7:51 PM // reply »
1 Comments

I agree with @Michael. I'd go further by saying that explicit code is better then implied.

Example of implied code is 'this' binding rules such as the method invocation rule or the combination of method invocation rule with baseless function rules.

This code requires multiple sweeps for human interpretation.


oky
Jan 4, 2013 at 2:56 PM // reply »
1 Comments

this is super! thanx man! took a little bit time, until i anderstood it! but now i can use it for my revealed modul instead. how is this pattern named?


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 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
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 »
InVision App - Prototyping Made Beautiful With Prototyping Tools