Watch on YouTube: youtu.be/OSZuNlVukDE


Call them what you will - auto-executing functions, Immediately Invoked Function Expressions, IIFE, auto anonymous functions, weird-looking function magic - but no matter what you call them they're still confusing.

Immediately Invoked Function Expressions (IIFEs) are the weird function wrappers at the head and tail of JavaScript files such as this one:

(function () {
  "use strict";

  console.log("Hello World!");
}());

I'm going to break this down to explain both the how and the why, but you'll have to forgive me because I can't do it in anything less than 5 minutes.

Note: If you want to follow along - and I highly recommend that you do - you should be using Google Chrome and open Chrome's JavaScript Developer Console from the menu icon in the upper-right-hand corner and select Tools and then JavaScript Console. In the window that pops up you should be on the Console tab. You may see some messages already in the Console. If so, hit the button that looks like an O with a strike through (the symbol you would commonly associate with do not, no smoking, or perhaps unavailable) - that's actually the clear(); button. Oh, and... yeah, you could have just typed clear();.

Let's start with HOW they work

Here are the major points

  • literals
  • expressions
  • logic (math)

a literal function

Let's consider a very basic literal function and its invocation:

function answer() {
  console.log(42);
}

answer();

Copy and paste that into Chrome's developer console. (P.S. if you wanted to type it out you would hold the <shift> key and then press <enter> key to create lines without running them immediately).

You should see that the output is 42.

a numeric expression

I'll switch gears for a moment to demonstrate what an expression is.

Copy and paste each of the following into the console and run them (by hitting ).

A number literal

42; // 42

A number expression

(42); // 42

An assignment (in which the right side becomes an expression)

var answer = 42; // 42

And now (going in reverse order) try running these:

answer.toString(); // "42"

And then

(42).toString(); // "42"

And lastly

42.toString(); // SyntaxError: Unexpected token ILLEGAL

Oh noes! My world is shattered! I don't know what's true anymore!!! (you're feeling that way, too, right?)

As it turns out there are cases where literals and expressions behave the exact same way, but also cases where they do not.

And assignment causes a literal to evaluate as an expression, just as enclosing parens () do.

(hint: both function and Number literals are in the same category).

Discrete Mathematics

Remember back in algebra when they told you that

2x + 2y = 2z

is the same evaluation as

2 (x + y) = 2z

because you can factor out a 2?

And remember how you can get

x + y = z

when dividing both sides by 2?

Well, sorry to bring back those high school memories darling, but it's the same principle here (except you don't get to ditch the parens)!

With this program, for example:

function say() {
  console.log("Hello World!");
}

say();

We can factor out say and get:

(
function () {
  console.log("Hello World");
}
)
();

And although you must keep the parens (think back to the example of 42.toString()), you can ditch the spaces:

(function () {
  console.log("Hello World!");
})();

But wait! There's more: No proper IIFE is complete without "use strict";

(function () {
  "use strict";

  console.log("Hello World!");
}());

To the careful observers, do you notice something I did that isn't just removing spaces?

If you didn't here's a hint (pretend we did u substitution on the function... where the u is an non-printing character): (()) vs ()()

Yeah, I shifted the paren to the outside. Now how that works... I have no idea, but I do believe that it's more aesthetic and so that's how I write my code. I won't argue if you feel differently.

The kind of evil people that punch babies

Lastly, out in the wild you will certainly find all manner of terrible hacks. Things that look like:

!function(){console.log("I'm a selfish hipster. I do what I want.")}()

or

+function(){console.log("My life goal is to confuse people!")}()

and maybe even

this,function(){console.log("I'm a moron... and I HATE people")}()

but probably not

null&function(){console.log("I sort all my B-trees with bogosort!")}()

Please, THINK OF THE CHILDREN!!! and don't be such a dolt if you grow up.

:-D

And now I make a movement as to WHY

JavaScript only has function scope. It doesn't have file scope (although NodeJS adds that) or block scope (if, for, while, switch, etc).

The purpose of IIFE is to give scope to a file or block.

And of course, you really can't talk about IIFEs without talking about "use strict;". The two go together like love and marriage (strikingly similar analogy, actually).

The use of IIFEs fall into two main categories:

  1. a commitment to quality reusable code (file-level, with use strict)
  2. a fleeting sense of excitment (for having done something out of order)

A Commitment to Quality

The whole purpose of an IIFE is to prevent you from making accidental mistakes.

For the simplest of demonstration of the problem, try running this in the console:

var a = 5
  , a = 6
  ;
a = 7;
var a = 8;

You would perhaps expect that an error would be throw to the effect of QuadrupleDeclarationError: What are you crazy!? You just defined 'a' 4 times!!, but no such luck in JavaScript...

I can hear you saying Now AJ, that's just silly. I mean, really! Who does that? and my response is of course You!. I mean, who's more likely to use a similar naming convention for functions and variables to you than yourself?

Here's a more practical example:

Let's say we have two JavaScript files and they both the function init to perform some action.

pirate-array.js:

function init() {
  fresh = true;
  arrR = [];
}

function pushObscenity(obscenity) {
  fresh = false;
  arrR.push(obscenity);
}

smurf-blocks.js

// them smurfs are trixy... don't even try to understand their
// parseInt-ing and bitshifting (>>) and bitwise operations (& | ~)
function init() {
  fresh = true;
              // red starts at bit 15 and ends at 11
  redMask   = parseInt("1111100000000000", 2);
              // green starts at bit 10 and ends at 5
  greenMask = parseInt("0000011111100000", 2);
              // blue starts at bit 4 and ends at 0
  blueMask  = parseInt("0000000000011111", 2);
  rgb = ((032 << 11) & redMask)
      + ((128 << 5) & greenMask)
      + ((007 << 0) & blueMask)
      ;
}

function addBlue() {
  b = (rgb & blueMask) >> 0;
  if (fresh) {
    b += 100;
    fresh = false;
  } else {
    b += 1;
  }
  rgb = (rgb & ~blueMask) | b;
}

There are so many problems in that example that it blows my mind! - and none of them are logical errors (I promise that is a valid way to add blue to a 16-bit rgb value - trust me, I've done it) - they're all due to JavaScript's confused nature!

The most immediate problem is that init is overwritten. Go ahead and copy all of both files in the Console and see for yourself.

init.toString();

When the first file loads, init will be created. As the second loads, it will be overwritten.

Realistically, you might not notice because there's a good chance that the first init is executed before the next loads... unless it's an init that only runs after the DOM is ready.

Most realistically, it's difficult to predict what will happen or when or why. Inconsistent behavior is bad behavior (and not the good kind, if you know what I mean).

Let's say we wrap our friends in IIFEs:

pirate-array.js

(function () {
  // pretend the stuff from above were here
}());

smurf-blocks.js

(function () {
  // same deal, a lot of pretending
}());

Now the init functions are scoped to the IIFE. We can keep them both.

But without "use strict";, we're still in grave peril. Because we forgot to declare fresh with var fresh;, it jumps out of scope and gets in bed with global.

Also, what looks like a harmless decimal representation of 032, with a leading 0 for the sake of readability is actually octal (base 8 instead of base 10)! And so is 007, but not even your tests would be able to tell!

With "use strict"; errors are thrown if

Fleeting Pleasure

The other uses of IIFEs are mostly hacks that are used out of ignorance, negligience, or misinformation (though there are rare legitimate uses).

Sometimes you're tempted to put a function where it doesn't belong - like in a loop.

For example, many a time people try to do something like this (except that instead of a setTimeout there would be some sort of web request or DOM manipulation):

var i
  ;

for (i = 0; i < 10; i += 1) {
  setTimeout(function () {
    console.log(i);
  }, 4);
}

And when that doesn't work (because it prints 10 ten times) they find some crazy example online that tells them to do this nonsense:

var i
  ;

for (i = 0; i < 10; i += 1) {
  (function (index) {
    setTimeout(function () {
      console.log(index);
    }, 4);
  })(i);
}

Which technically works and is cute and all, but... no, actually it's not even cute. It just looks like a confusing mess.

The only reason that that even works at all is because there's magic going on behind the scenes that hoists the anonymous function up to the top (just like with variables) and then calls that function reference.

So skip the confusion, and just do this instead:

function thinkBeforeSpeaking(index) {
  setTimeout(function () {
    console.log(index);
  }, 4);
  // 4 ms isn't a lot, but it's more than I usually take
}

var i
  ;

for (i = 0; i < 10; i += 1) {
  thinkBeforeSpeaking(i);
}

Although I'm not as big on thinking before speaking (you might stiffle a lot of good creativity or necessary criticism that way), I am big on writing code that makes sense and is error-free whenever possible.

Conclusion

JavaScript shouldn't be your first language. Go learn python, then hop on rails, and come back once you can easily tell good ideas from bad ones.

(or, if you must JavaScript, at the very least have a tutor and or pair program)


By AJ ONeal

Was this useful to you? Share it!

Also, you can give me a tip or hire me.


blog comments powered by Disqus

Published

2013-02-08


Help a brother out.


Categories


Tags


I'm available for hire (and I appreciate tips)