JavaScript: Self-invoking anonymous function blocks

JavaScript: Self-invoking anonymous function blocks

We have learnt scopes and closures in JavaScript with my previous posts. Combining these two core concepts, JavaScript gives us a beautiful syntax called self-invoking anonymous functions. It simply means an unnamed function that executes itself. This special syntax can give us some extraordinary powers 💪,

  1. Power to expose only selected set of variables and methods to its outer scope.
  2. Power to close down on variable and method name clashes with other code blocks or third party libraries.
  3. Power to control JavaScript asynchronous executions.
  4. With all the above powers, comes the greater responsibility of writing your open source libraries for JavaScript using self-invoking anonymous function blocks.

Now let's take a look at its syntax. It's very simple.

(function() {

})()
Syntax of self-invoking anonymous function

The syntax is self-explanatory. Why self-invoking? Because the last () brackets executes the function. Why anonymous? Because the function doesn't have a name. You can have as many anonymous functions as you want in JavaScript.

One rule to declare anonymous functions is to have a wrapping () brackets. In other words, you can pass an anonymous function where ever you see a wrapping () brackets. Eg,
function executeAnonymousFunction(fn) {
	fn(); //Prints hello world when our anonymous function gets invoked at this place. 
}
executeAnonymousFunction(function() { // Note: anonymous fn passed as parameter
	console.log("Hello world");
});

or

setTimeout(function() {
	console.log("hello world"); //prints hello world after 1 second.
}, 1000);
Anonymous functions within ()

Now let's take a look at how self-invoking anonymous functions or self-executing anonymous functions give you all the powers that we talked about earlier.

Power to expose a selected set of variables and methods to the outer scope.

Let's take a code example,

var myLibrary = (function() {
  var privateVariable = "I'm a private variable";
  var publicVariable = "I'm a public variable"
  
  function privateMethod() {
    console.log("I'm a private method");
  }
  function publicMethod() {
    console.log("I'm a public method and I have access to:");
    console.log(privateVariable);
    console.log(privateMethod());
  }

  // Expose only public items
  return {
    publicMethod: publicMethod,
    publicVariable: publicVariable
  }
})();

myLibrary.publicMethod(); // Prints all console.logs
console.log(myLibrary.publicVariable); // Prints I'm a public variable

console.log(myLibrary.privateVariable); // undefined
myLibrary.privateMethod(); // Uncaught TypeError: myLibrary.privateMethod is not a function
Encapsulate methods and variables

You can see that the private methods and variables are well encapsulated within our anonymous function, make themselves available within the block, but not outside.

Power to close down on variable and method name clashes with other code blocks or third party libraries.

Again let's look this through code example,

var myLibraryOne = (function() {
    var bar = "Hello world"; // same variable name 
    function foo() { // same method name
    	console.log(bar);
    }
    // some other operations
    return foo;
})();
var myLibraryTwo = (function() {
    var bar = "Hello world"; // same variable name
    function foo() { // same method name
    	console.log(bar); 
    }
    // some other operations
    return foo;
})();
Prevents variable and method name clashes

Both of those self-executing anonymous functions have same-named variables and methods. But they never complain that the variable is already declared. Thus it avoids clashes with other libraries and code blocks.

Power to control JavaScript asynchronous executions.

Let's take this code,

for (var i = 0; i < 5; i++) {
    setTimeout(function() { // asynchronous operation
    	console.log(i);
    }, 1000 * i);
}
Asynchronous code with bug

I have been asked this exact question in many interviews. Anyone assessing the code would think the console.log will print,

prints: 0 // after 0 second
prints: 1 // after 1 second
prints: 2 // after 2 seconds
prints: 3 // after 3 seconds
prints: 4 // after 4 seconds

And if you have answered the above, the interviewer might have kicked you out of the door! And if he doesn't, you please kick him out.

The answer for the above code is quite different and it is,

prints: 5 // after 0 seconds
prints: 5 // after 1 seconds
prints: 5 // after 2 seconds
prints: 5 // after 3 seconds
prints: 5 // after 4 seconds

Yes, it prints '5' five times at various intervals!

This is because the JavaScript event loop executes the code in a unique order. It priorities synchronous operations (in this code example it is the for loop) over asynchronous operations. So, the for loop registers all setTimeout for later execution. By the time for finishes, the value for variable i is 5 in the memory. Then comes the asynchronous operations like setTimeout. And this is why the console.log can only read 5 for variable i from the memory.

How can we fix this? The magic power here is the self-invoking anonymous functions. How is it going to help? let's take a look,

for (var i = 0; i < 5; i++) {
    (function(i) {
        setTimeout(function() { // asynchronous operation
            console.log(i);
        }, 1000 * i);
    })(i); // This is how we pass parameters to self-invoking functions
}
Asynchronous code fixed with self-invoking anonymous function

The above code will print exactly the result we wanted,

prints: 0 // after 0 second
prints: 1 // after 1 second
prints: 2 // after 2 seconds
prints: 3 // after 3 seconds
prints: 4 // after 4 seconds

Self-invoking anonymous functions give an individual private scope of i for each setTimeout we register to JavaScript event loop or eval loop. It means we have five times i declared with different values from 0 to 4 in the memory, each with its scope wrapped. Pure magic 🧝‍♀️!

In real-world project building, you might be asked to write a code to fetch a list of songs from an API and for each song, you will be asked to fetch its artist name using a different API. Something like this,

fetchSongsList(function(songs) {
    for (var songIndex=0; songIndex <= songs.length; songIndex++) {
    	fetchSongArtistBySongId(songs[songIndex].id, function(artist) {
            //Artist for song blah blah.
        });
    }
});
Real world example - bug

If you are following my post keenly, you would have immediately caught what's wrong with the above code. How to fix it? With the magic self-invoking anonymous function voila!

fetchSongsList(function(songs) {
    for (var songIndex=0; songIndex <= songs.length; songIndex++) {
        (function(song){
            fetchSongArtistBySongId(song.id, function(artist) {
                //Artist for song blah blah.
            });
        })(song);
    }
});
Fixed code with self-invoking asynchronous function

Last but not least,

Power of writing your libraries

Great soles who wrote popular libraries like JQuery, underscore or lodash would have started writing their first line of code with self-invoking anonymous function for their libraries. Request you to go and read the open-source library's codebase. You can learn a lot more from them.

Now that you know everything about it, you have the power to start writing your JavaScript library.

If you have reached this far, precious 💍 things in life will find your way.

With that note, adios.

Show Comments