IIFE
An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.
Use cases-
Helps avoid polluting the global namespace-
- Since our application may incorporate a large number of functions and global variables from various source files, it's critical to keep the number of global variables to a minimum.
- We could utilize the IIFE pattern if we have some initiation code that we don't need to use again. Because we won't be reusing the code, IIFE is preferable than a function declaration or a function expression in this scenario.
Example-
(function () {
// some initiation code
let firstVariable;
let secondVariable;
})();
firstVariable
and secondVariable
will be discarded after the function is executed.
The module pattern-
- We would also use IIFE to create private and public variables and methods.
- These patterns were more useful before the introduction of ES6, when we didn't have the
let
and theconst
keywords. Back then when we imported all the JavaScript files into one, then there were a lot of conflicts in variable names since all variables were global because of declaration usingvar
. Thus developers used IIFE module patterns where the variables were made and only those required inside module were left in global scope and others were discarded because of property of Scope using IIFEs. This also overlaps with the first use case of IIFEs mentioned above. Consider this example to understand better-
Example-
As you know that a function in JavaScript creates the local scope. So, you can define variables and function inside a function which cannot be access outside of that function. However, sometime you accidently pollute the global variables or functions by unknowingly giving same name to variables & functions as global variable & function names.
For example, there are multiple
.js
files in your application written by multiple developers over a period of time. Single JavaScript file includes many functions and so these multiple.js
files will result in large number of functions. There is a good chance of having same name of function exists in different.js
files written by multiple developer and if these files included in a single web page then it will pollute the global scope by having two or more function or variables with the same name.
Consider the following example of MyScript1.js
and MyScript2.js
with same variable & function name.
MyScript1.js
var userName = "Bill";
function display(name)
{
alert("MyScript1.js: " + name);
}
display(userName);
MyScript2.js
var userName = "Steve";
function display(name)
{
alert("MyScript2.js: " + name);
}
display(userName);
Importing both files-
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>JavaScript Demo</title>
<script src="/MyScript1.js"></<script>
<script src="/MyScript2.js"></<script>
</head>
<body>
<h1> IIFE Demo</h1>
</body>
</html>
If you run above example, you will find that every time it call
display()
function inMyScript2.js
becauseMyScript2.js
included afterMyScript1.js
in a web page. So JavaScript considers last definition of a function if two functions have the same name.IEFE solves this problem by having its own scope and restricting functions and variables to become global. The functions and variables declare inside IIFE will not pollute global scope even they have same name as global variables & functions. So let's see what is an IIFE is.
Advantages of IIFE:
- Helps avoid creating unnecessary global variables and functions.
- Functions and variables defined in IIFE do not conflict with other functions & variables even if they have same name.
- Organize JavaScript code.
Make JavaScript code maintainable.
Even though the information above is more than enough and well explained to grasp the concept but you can still check out this documentation and this article to read more in-depth about IIFEs.*
this
keyword-
this
represents the object that the function is a property of.
or simply
this
helps refer to the object it belongs to.
- In a method,
this
refers to the owner object.
Example-
Code-
const person = {
firstName: "Pranav",
lastName : "Mendiratta",
fullName : function() {
// here `this` keyword refers to our object `person`
return this.firstName + " " + this.lastName;
}
};
console.log(person.fullName())
Output-
"Pranav Mendiratta"
- Alone,
this
refers to the global object (called the window object in the browser).
Example-
Code-
console.log(this)
Output-
window
- In a function,
this
refers to the global object. - In a function, in strict mode,
this
is undefined. - In an event,
this
refers to the element that received the event.
Example-
Code-
<button onclick="this.style.display='none'">
Click to Remove Me!
</button>
Tricky example on this
keyword 1
Output-
window
window
c
Explanation-
- Both
a
andb
are functions of the global/ window object, thus as per the definition, thewindow
object gets returned.
this
represents the object that the function is a property of.
The third
console.log
returns thec
object because that's what has called thehi()
function inc.hi()
.One trick to solve these easily is to check what is on the left side of the function call. If there is nothing then it returns the window object. If some object is calling it like
c.hi()
then thethis
keyword in the function points to the objectc
.
Tricky example on this
keyword 2
Output-
obj
window
Explanation-
- On calling the
sing()
function, theconsole.log(this)
on line 4 returns theobj
object sinceobj
is calling the function. - Whereas the
console.log(this)
on line 6 returns thewindow
object because its function call is not attached to any object, and those not attached are always under the global/ window object.
Tricky example on this
keyword 3
Output-
b
window
d
Explanation-
- Using the trick we learned in tricky example 1, we see that
b.say()
should return theb
object and it does exactly that. - Arrow functions are lexically scoped where as regular anonymous functions are dynamically scoped.
- That is why when calling
c.say()()
, it returns the window object because it uses anonymous functions that are lexically scoped (we've learned in earlier part of the series). > Lexical scope care where a function was declared, but dynamic scope cares where a function was called from. - This statement will help in understanding the difference.
- The final output on calling
d.say()()
returns the objectd
that is the correct output because it used the arrow functions that are dynamically scoped and bind thethis
keyword with the object calling the function.
Tricky example on this
keyword 4
const phone = function (model, brand){
this.model = model,
this.brand = brand
}
// regular anonymous function used
phone.prototype.clickPicture = function(){
console.log(`${this.brand} ${this.model} clicks picture!`)
}
// arrow function used here
phone.prototype.powerOn = () => {
console.log(`${this.brand} ${this.model} boots up!`)
}
const iphone = new phone("Iphone 12", "Apple")
console.log(iphone.clickPicture())
console.log(iphone.powerOn())
Output-
"Apple Iphone 12 clicks picture!"
"undefined undefined boots up!"
Explanation-
-
Arrow functions are lexically scoped where as regular anonymous functions are dynamically scoped that is why the arrow functions that are dynamically scoped and bind the
this
keyword with the object calling the function and the other function doesn't thus logging undefined on usingthis.brand
orthis.model
.
Conclusion-
- A lot of the weird behavior of the
this
keyword is mainly because it is dynamically scoped and not lexically scoped like everything else in JavaScript meaning that it is not important where it is written but how it is called.
Solution to weird behavior-
- One way to solve these issues is the use of arrow functions that were introduced in ES6.
- If we use an arrow function in the previous example then our function gives us the desired output.
- Another way is to bind the
this
keyword to the object. We will learn more aboutbind
keyword ahead.
Credits- Iqbal M Ipel
Credits- Sanjeev Sharma
call()
With the
call()
method, you can write a method that can be used on different objects.
Example-
Code-
const wizard = {
name: 'Pranav',
health: 100,
heal: function(num1, num2) {
this.health += num1 + num2;
}
}
const archer = {
name: 'Robin',
health: 50
}
wizard.heal.call(archer, 50, 60)
console.log(archer)
Output-
{
health: 160,
name: "Robin"
}
apply()
With the apply() method, you can write a method that can be used on different objects.
- It is very similar to the
call
keyword, only difference is that the arguments are passed as an array when we are usingapply
.
Example-
Code-
const wizard = {
name: 'Pranav',
health: 100,
heal: function(num1, num2) {
this.health += num1 + num2;
}
}
const archer = {
name: 'Robin',
health: 50
}
wizard.heal.apply(archer, [20, 30])
console.log(archer)
Output-
{
health: 100,
name: "Robin"
}
bind()
The bind() method creates a new function that, when called, has its this keyword set to the provided value.
It let’s us explicitly define the value of this when calling a function.
It returns a new function that we can call.
Example-
Code-
const wizard = {
name: 'Pranav',
health: 100,
heal: function(num1, num2) {
this.health += num1 + num2;
}
}
const archer = {
name: 'Robin',
health: 50
}
const healArcher = wizard.heal.bind(archer, 50, 60);
healArcher()
console.log(archer)
The js engine is creating a new instance of the heal function and binding its this
object to archer.
Output-
{
health: 160,
name: "Robin"
}
Credits- Himanshu Satija
Currying-
Currying is a technique of evaluating function with multiple arguments, into sequence of functions with single argument.
Example 1-
Code-
function volume(length) {
return function(width) {
return function(height) {
return height * width * length;
}
}
}
console.log(volume(11)(2)(3))
Output-
66
Example 2-
Code-
function sum(a, b) {
return a+b;
}
var sumWithThree = sum.bind(this, 3);
console.log(sumWithThree(4));
Output-
7
Partial Application-
Partial application starts with a function. We take this function and create a new one with one or more of its arguments already “set” or partially applied. It will help reduce the number of parameters needed for our functions.
Both currying and partial application are patterns that allow us to call functions with some of their parameters, and provide the rest later.
They both are important concepts in Functional programming.
Example-
Code-
const multiply = (a, b, c) => a * b * c
const partialMultiplyBy5 = multiply.bind(null, 5)
partialMultiplyBy5(10, 20)
Output-
1000
Difference b/w Currying and Partial Application-
- Partial application is more or less a pattern of calling a function. You can partially apply any function.
- Currying is more about a form of the function. To be able to use currying, you have to explicitly create a new function that is a curried version of the original one.
Advantages of using Currying or Partial Application-
- They both help us create specialized versions of generic functions, thus removing duplication and making the code easier to compose.
- Another benefit of using partial application and currying is that they can help us create more readable code.
Connect with me-
Appendix-
- Advanced JavaScript Series - Part 1: Behind the scenes (JavaScript Engine, ATS, Hidden Classes, Garbage Collection)
- Advanced JavaScript Series - Part 2: Execution Context and Call Stack
- Advanced JavaScript Series - Part 3: Weird JS behavior, Strict Mode and Hoisting, Temporal Dead Zone
- Advanced JavaScript Series - Part 4.1: Global, Function and Block Scope, Lexical vs Dynamic Scoping
- Advanced JavaScript Series - Part 4.2: Scope Chains and their working, Lexical and Variable Environments
- Advanced JavaScript Series - Part 5: IIFE & 'this' keyword in JS(tricky Eg.), call(), apply(), bind(), Currying(Functional Prog)
- Advanced JavaScript Series - Part 6.1: Everything in JS is an Object? Weird JS behaviors revealed, Primitive Non-Primitive Types
- Advanced JavaScript Series - Part 6.2: Pass by Value & Pass by Reference, Shallow & Deep Copy, Type Coercion
- Advanced JavaScript Series - Part 7: First Class Citizens & Higher Order Functions
- Advanced JavaScript Series - Part 8: The 2 Pillars~ Closures & Prototypal Inheritance
- Advanced JavaScript Series - Part 9: Constructor Functions, Object Oriented,
new
keyword
References-
- https://developer.mozilla.org/en-US/docs/Glossary/IIFE
- https://www.tutorialsteacher.com/javascript/immediately-invoked-function-expression-iife
- https://www.w3schools.com/js/js_this.asp
- https://www.w3schools.com/js/js_function_call.asp
- https://www.w3schools.com/js/js_function_apply.asp
- https://medium.com/@omergoldberg/javascript-call-apply-and-bind-e5c27301f7bb
- https://www.tutorialspoint.com/what-is-currying-in-javascript
- https://www.freecodecamp.org/news/how-to-use-partial-application-to-improve-your-javascript-code-5af9ad877833/
- https://medium.com/dailyjs/functional-js-5-partial-application-currying-da30da4e0cc3
- https://medium.com/@osmanakar_65575/javascript-lexical-and-dynamic-scoping-72c17e4476dd#:~:text=The%20Key%20Contrast%20Between%20Lexical,a%20function%20was%20called%20from.
Top comments (6)
Thankyou for great explanation 👍
Thank you for the complement😊
@pranav016 : Can you please revisit lines mentioned in tricky JS part 3.
I have a understanding that anonymous function are dynamically scoped and arrow functions are lexical scoped.
Using the trick we learned in tricky example 1, we see that b.say() should return the b object and it does exactly that.
Arrow functions are lexically scoped where as regular anonymous functions are dynamically scoped.
That is why when calling c.say()(), it returns the window object because it uses anonymous functions that are lexically scoped (we've learned in earlier part of the series). > Lexical scope care where a function was declared, but dynamic scope cares where a function was called from.
This statement will help in understanding the difference.
The final output on calling d.say()() returns the object d that is the correct output because it used the arrow functions that are dynamically scoped and bind the this keyword with the object calling the function
Even I got confused by the given statement but here is how I understood the example breaking it into line by line:
When we are doing
c.say()()
, it means we are executing the returned value of thesay()
method.c.say()()
is nothing but a shorthand for:// ......
const E = c.say();
E();
....... //
Here, E is a function that is storing the value returned by method say() i.e.
function() { console.log(this) }
(notice the return keyword invar c = {....., say() {return .... } }
).So, if you do
console.log(E)
, it will print the definition of the returned function, like this:ƒ () { console.log(this) }
and now to execute and get the value of 'this', call the function E like this
E()
, which will result inwindow
.The reason is that
ƒ () { console.log(this) }
has no reference to the object upon which it is called. It simply points to the window (in the case of the browser) object.Had the code been like this:
var c = {
name: 'Pranav',
say() {
function innerFunction() {
console.log(this);
}
innerFunction();
}
}
and you call on it as
c.say();
, this would have also resulted in output as 'window object'.Hence the statement in the above article for this particular code part can be summed as :
I hope this explanation helps others too!
great read!
Thank you 🙂