It was really a hard concept to understand and it took me a while to comprehend what closures are. So here is my understanding of what it is with diagrams and code snippets for better visualisation
To warm up my brain, I will start with an example that doesn't involve closures and slowly change my example to involve closures at the end
function add1(){
var x = 1;
var f = function(y){
return x + y;
}
return f(3);
}
console.log(add1());
Here we have a simple function called add1
.
- It has a local variable
x
which has a value of1
- Another variable
f
which is assigned a function - This
add1
function returns the functionf
So if we run this code with it will return 4
with x
= 1 and y
= 3
Since we have a function declaration and that function is called in the console.log. The execution context will be as follows:
So first of all, we will have the global execution context
being created. We will have the creation
and execution
phase. In the creation
phase, we will hoist the add1
function to the top of the scope and in the execution
phase we will execute the add1
function
Calling the add1
function will create add1
execution context and it will be as follows:
In this execution context, we will hoist the variables x
and f
in the creation
phase and both are undefined in the creation
phase. In the execution
phase, x
is assigned the value of 1
and f
is assigned to the function that accepts a argument y
. After assignment, it is then returning function call f(3)
;
This creates another execution context which is called f. In f
execution context, we are not hoisting anything in the creation phase as there are no variables or function declared. In the execution phase, it is returning x+y
where the value of x
is retrieved from climbing the scope chain in the add1
execution context and the value of y
is passed in by f(3)
So in overall x
is 1
and y
is 3
. Returning 4
. After each EC is executed, it is then popped off the stack one by one.
Let's take a look the scope chain
In the f
execution context, it is returning x+y
. The value of y
is readily available in this context being the value of 3
but the value of x
is not. So it reference the outer execution context and retrieve the value of x
which is 1
.
Phew.. That was alot. Hopefully you are able to grasp the execution context and scoping. So now we are ready to move on to something a bit more complicated that is related to closures. Here is the code snippet
var add = function() {
var x = 1;
var f = function(y) {
return x + y;
};
return f;
};
var g = add();
console.log(g(3));
This example is different from the first one where we are now returning a reference to the function and not the value. Then we are calling the function outside the scope of the function add
when g(3)
is called. Probably this part might be hard to grasp.
So let's go through the execution context once more and see how this adds up. First the global execution context
Followed by the add
execution context
Now comes the interesting part. Once the add
execution context finish executing, it will be popped off the stack
Next will be the g(3) execution context. Remember that the variable g
contains the function f
since that is the function that was returned in add
function.
So g
will return x + y
.We are running g(3)
so value of y
will be 3.
What about the value of x
?
Well if we look around, we won't be able to find x
. But we remember that x
was set to 1
in another execution context that was created, executed and popped off.
So in Javascript
, if a function is created inside another function like g
. So g
will keep a reference to variables that were in the scope of the enclosing function in our case add
. So meaning g
still has got access to the memory of execution context of add
and that includes the variable x
.
Basically, the add
execution context has ended, but g
is still allowed to access the variables of the execution context which are still in the memory of the execution context of add
.
Our function g
can still climb up the scope chain and find x
although it's not in the execution stack anymore.
g's
execution context has closed in
x
which is an outer variable even if the x's
execution context is gone. So our function g
is a closure
.
Closures like the g
function internally store references to outer variables.
Let's take a look at the scope chain for this
We have the global execution context scope. We will have the add
and g
variable and their execution context.
After line var g = add();
finishes, the add execution context gets popped off the stack.(It's shaded for this reason)
We then create and execute the g
execution context. Where it will still be able to reference the x
outer variable even after the x's
execution context is not on the stack anymore
Hopefully you were able to get some idea on what a closure is from this article. If you're still confused, please do read the following reference.
Here is the main reference that I used to further my understanding on this topic
Ultimate guide to execution context
Top comments (3)
Nice post and how I look at Closures:
When the main function is executed and some variables are assigned a return function,
even later by calling the returned function, we can access the variables that was in the call when the return function returned.
After the execution of the main function, the Scope chain is kept and referred to and is considered to be the parent of the function that is returned and which will be later called.
Small sidenote, if you label code snippets, they will look ð¥.
Oh wow! Cool! Thanks for the advise ð