This is the first blog post in the series of two posts on managing scope in JavaScript. Variables and functions created on global scope are dangerous due to the possibility of being overridden from other parts of the application. This might not be the problem in a small side project where you and probably one of your buddy are showcasing your JavaScript skills by making some cool stuffs, this will certainly be a problem in a real project with many people working on different JavaScript files. We will first understand what an IIFE is and will later discuss a real world example with and without IIFE.
Immediately Invoked Function Expressions (IIFE, pronounced iffy)
An IIFE is an anonymous function contained within a pair of parenthesis and is invoked immediately. The pair of parenthesis creates a local scope for all the code inside of it and makes the anonymous function a function expression. This justifies the name “Immediately Invoked Function Expression”. If you are not familiar with function expressions, then I will encourage you to read my post Understand functions in JavaScript.
A typical IIFE looks like this.
(function() {
}());
Some people also write it like this.
(function() {
})();
I prefer the first style and that’s what Douglas Crockford suggests. So all my explanations will be based on the first style.
The outermost pair of parenthesis turns everything inside of it to an expression because parentheses can’t contain JavaScript statements. The other pair of parentheses after function definition invokes the function immediately. Let’s look at an example.
(function() {
alert("I created my first IIFE today");
}());
The function above will be invoked automatically and show the message in a message box. We will try to understand this IIFE in bits and pieces. Let’s start with writing the function you want to execute.
function() {
alert("I created my first IIFE today");
}
This is an anonymous function for which JavaScript will report an error because this anonymous function syntax is not valid. But we can live with that for the moment as our IIFE is not completed yet. Now we will add a pair of parenthesis in the end of the function body to make it invoke immediately.
function() {
alert("I created my first IIFE today");
}()
The last step is to wrap everything in another pair of parenthesis to make this a function expression.
(function() {
alert("I created my first IIFE today");
}());
Any variables or functions defined within IIFE block are local to the block and it’s not possible for any code outside this scope to change them.
(function() {
var x = "Hello";
console.log(x);
}());
console.log(x);
It is also possible to pass arguments to IIFE. This is how we do that.
(function(firstName, lastName) {
var fullName = firstName + " " + lastName;
console.log(fullName);
}("Jim", "Cooper"));
The values “Jim” and “Cooper” are passed to function as firstName
and lastName
and are used to construct fullName
.
Now that we understand IIFE, let’s take an example of a product details page in an e-commerce application and see how IIFE solves some serious problems.
You can think of a global variable productName
in an e-commerce application that was created in global scope in a JS file running on product details page. Product details page might be executing a whole lot of JavaScript code spread across multiple JS files. While script to load and display the product information might be written in one file, script to add product to the cart may exist in some other file. There could be a dedicated JS file to load and display the product reviews. The point here is, web pages in real world applications today runs a whole bunch of JavaScript files to handle different scenarios.
The product name is such a common term that more than one JS file on the product details page may have a variable named productName
. If variables are not created with local scoping in place, then chances are high that the variable productName
gets modified accidentally in some other file.
The real world applications are implemented with proper scoping in mind. I hope at this point you would agree that global variables are evil in JavaScript and creating global variables is a bad practice. Let’s see an example and try to understand the problem with the code.
function getPhone() {
return "iPhone7";
}
function displayProductDetails(productName) {
console.log("This is " + productName);
}
function addProductToCart(productName) {
console.log(productName + " added to your shopping cart");
}
function makePayment(productName) {
console.log("Making payment for " + productName);
}
function getRelatedProducts() {
var relatedProducts = ["Headphone", "Phone cover", "Tempered glass"];
return relatedProducts;
}
function suggestMeAHeadphone() {
var relatedProducts = getRelatedProducts();
productName = relatedProducts[0];
return productName;
}
var productName = getPhone();
displayProductDetails(productName);
addProductToCart(productName);
var headPhone = suggestMeAHeadphone();
makePayment(productName);
The example above demonstrates a product details page where product details are loaded and displayed in the user interface. User can add the product to the cart. User can optionally select a product from related products suggestion and may add to the cart (I haven’t written code for adding related product to the cart for the simplicity). Finally, user makes the payment for the product (Phone).
This is not an example you will appreciate a lot but the intention here is to understand the problems caused by global variables. If you see the code carefully, the call to function getPhone
returns a product which is stored in a variable productName
. Since the variable is outside of any function, it will exist in global namespace.
Now look at the call to suggestMeAHeadphone
function that happens just before makePayment
. suggestMeAHeadphone
function makes a call to the function getRelatedProducts
, assigns the first product from the list of related products to variable productName
and returns the productName. You might have noticed that variable productName
has been assigned a new value, the name of the headphone in the function suggestMeAHeadphone
. Finally, the function makePayment
will be called to finalize the checkout process. But, due to all the evilness of global variable productName
, makePayment
function will write the statement “Making payment for Headphone” while it is supposed to write “Making payment for iPhone7”.
Combat this with IIFE
Now since you know the basics of Immediately Invoked Function Expressions, let’s try to write our e-commerce example using IIFE and avoid the variables from being accessible to the code outside of the local scope. I will put all the code in an IIFE except the code for handling “Related products”. Have a look.
(function() {
function getPhone() {
return "iPhone7";
}
function displayProductDetails(productName) {
onsole.log("This is " + productName);
}
function addProductToCart(productName) {
console.log(productName + " added to your shopping cart");
}
function makePayment(productName) {
console.log("Making payment for " + productName);
}
var productName = getPhone();
displayProductDetails(productName);
addProductToCart(productName);
makePayment(productName);
}());
I will again say that this code is not very realistic as many things like adding product to the cart and making payment will happen when user clicks on some button in user interface. I kept the example short and simple to focus on the core of our discussion.
I will put the remaining of the code dedicated to handle “Related products” in a different IIFE, which may exist in a different JS file altogether.
(function(){
function getRelatedProducts() {
var relatedProducts = ["Sony Headphone", "Phone cover", "Tempered glass"];
return relatedProducts;
}
function suggestMeAHeadphone() {
var relatedProducts = getRelatedProducts();
var productName = relatedProducts[0];
console.log("We suggest you to buy " + productName);
return productName;
}
var headPhone = suggestMeAHeadphone();
}());
By achieving local scoping with IIFEs, our variable productName
is safe now. Though I have used var
keyword before productName
in the second IIFE, missing out the var
will create a new global variable productName
and the same variable in first IIFE is still safe. If you just ignore the quality of the e-commerce example I have presented, you must be able to understand the IIFE by now and their use in avoiding global scope pollution. Most of the modern JavaScript libraries like jQuery, Backbone use IIFE pattern to keep all the code in local scope.
That’s it!
I hope you enjoyed reading this article. This was the first and introductory post on managing the scope in JavaScript. In the next post I will take you through the Module and Revealing Module patterns to achieve the implementation of private variables and functions.
Do you have any questions or was there anything unclear? Do leave your comments. I will end up this post with an IIFE wishing you something.
(function() {
alert("Happy JavaScripting!!");
}());
You might also be interested in: