What is a Closure?
A closure is a combination of a function bundled (enclosed/packed) together with references to its lexical environment.
In layman's terms, a closure is a concept in javascript that provides you access to an outer function's scope and its parent functions as well, from an inner function.
function demo() {
var name = "Dog"; // name is a local variable created by demo
function displayName() {
// displayName() is the inner function, that forms the closure
console.log(name); // use variable declared in the parent function
}
displayName();
}
demo();
// Output - Dog
Here demo() creates a local variable called name and a function displayName(). There is no local variable inside the function displayName(), instead, it is inside the demo() function. Here the inner function displayName()
has access to the outer function demo() this is also an example of lexical scoping which we will discuss later.
So the fundamental idea at a high level is, we have to get access to the variables from the outer functions/scope where these variables were defined and now the outer function/scope is already returned and the execution context has been out of call stack.
I know this is still confusing and how it is happening is still not clear, just go along and everything will be fine. So are you ready? Let's go!
What is an Execution Context?
The browser doesn't understand the high-level javascript code which is written in our application. It needs to be converted into the machine code format which our browser and computer can understand natively.
So whenever the browser encounters a JavaScript code, it sends it to its JavaScript Engine. So then, this JavaScript Engine creates an environment to handle the execution of this code, this special environment is known as
Execution Context.
Note - The Browsers maintain the Execution Contexts using the Call Stack. And only 1 Execution Context can be run at a time, so that's why JavaScript is a single-threaded language.
At any point in time, only 1 execution context can be running, the Execution context contains code that needs to be executed and whatever is needed to run the code.
Types of Execution Contexts
Global Execution Context -
Whenever the Javascript Engine receives a JavaScript file it creates a Global Execution Context and pushes it onto the Call Stack. Any code that is not inside a function is executed in this Context.
There can be only 1 GEC for a particular JavaScript file.
Function Execution Context -
Whenever a function is called a new Execution Context is created within the Global Execution Context and this is called Function Execution Context, later pushed onto the Call Stack.
How are Execution Contexts created?
There are 2 phases involved in the creation of Execution Contexts,
Creation Phase
Execution Phase
In the Creation Phase, the references to the variables and functions defined are allocated to the memory of the current execution context. The variable references in the initialization phase are assigned a special keyword called undefined
and the functions are assigned their value as it is.
When the Execution phase starts and every line of code is run sequentially and the references to the variables are assigned their particular value.
The current running Execution Context is the top item of the Stack and when the execution of the current execution context is finished it gets popped out of the Call Stack.
Closures in for Loops
One of the most widely used statements is for() loop
โฟ and setTimeout()
is often used for delaying โ the execution of the code ๐จโ๐ป. Together they can blow your mind ๐คฏ.
Let's say we need to sequentially print numbers from 1 to 5.
for(var i =1; i<=5; i++) {
console.log(i); // 1 2 3 4 5
}
Let's say we need to have a delay of 1sec after each output, so let's bring our warrior setTimeout.
for (var i=1; i<=5; i++) {
setTimeout(function() { console.log(i); }, 1000*i);
// 6 6 6 6 6
}
WTH!๐ซ what did just happen? We thought the answer would be
// 1 2 3 4 5
There is something that we need to know, inside the setTimeout we are passing the reference to the variable i, not the value itself. To address this issue we can use let, to overcome this issue.
for(let i=1; i<=5; i++) {
console.log(i); // 1 2 3 4 5
}
why does this happen? To understand this we have to look more at scoping. Because of the Block scope of the let variable, every time a new reference to the variable i
is created because of the block scope of let, unlike var
where there is a formation of closure of, i with the loop and the reference of value i
is remembered, the value of the last state of i
is displayed. (Pphhh... a bit complicated to explain, but you'll get it)
Variable Scope in JavaScript
In JavaScript variable scope means, where can we use which variable?
Variables can be present in global scope
/ inside a block
/ inside a function
.
What is a block?
Any piece of code which is inside { }
(curly braces) is called a block.
// Block
{
const name = 'A Demo Name';
let num = 69;
}
Types of Scope
Block Scope
Functional Scope
Global Scope
If there is a case where you want the variable to be not accessed outside the block, then always use let
and const
instead of var
which has a global scope.
So for the short understanding of block scope and function scope is,
If we declare a variable with let/const
keyword inside a block {}, and do not want any other piece of code to get access to it, then it is called block scope. Similarly, if we declare the variables inside a function and want to restrict its accessibility inside that function itself then it's called function scope.
References
MDN Docs are the best place to get an even better insight into any topic.
Hope you liked it and thanks for being with me till now. If you liked it, kindly consider subscribing and liking the post. ๐๐๐ป