DEV Community

Cover image for A Simple Explanation of JavaScript "Closures"
Pegipin
Pegipin

Posted on • Updated on

A Simple Explanation of JavaScript "Closures"

The first thing that we need to know about closures is that a closure is not a feature that we explicitly use. So we don't create closures manually, like we create a new array or a new function. So a closure simply happens automatically in certain situations, we just need to recognize those situations.

Most of the times when we run code in other languages we can not access variables outside of a function inside of that function, that is just not always possible in other languages but it is possible in javaScript and it is what we call a closure.
Let's Start with this simple code:

let a = 1;

function print() {
  console.log(a);
}

print();
Enter fullscreen mode Exit fullscreen mode

It printed number 1. This is actually a closure.

We have an inner scope of the function:

function print() {
  console.log(a);
}

Enter fullscreen mode Exit fullscreen mode

And this inner scope is accessible in variable a which is inside the outer scope.
Now, if we take a and reassign it to 2 before we call print() function, now it prints out number 2.

let a = 1

function print() {
  console.log(a);
}

a = 2
print();
Enter fullscreen mode Exit fullscreen mode

This is because this print() takes whatever the most recent value of a is, whenever we call print().

But usually closures are defined as functions inside of other functions;

function print(variable) {

  return function func(variable2) {
    console.log(variable);
    console.log(variabl2);
  }
}
Enter fullscreen mode Exit fullscreen mode

we are calling function print which takes a variable and is returing a brand new function which is going to log out a variable from print as well as a variable2 from func function.
Now:

function print(variable) {

  return function func(variable2) {
    console.log(variable);
    console.log(variable2);
  }
}

let a = print(1);
Enter fullscreen mode Exit fullscreen mode

This is doing as calling function print(), so a is a new function. Now we call a() and we pass to it 2

function print(variable) {

  return function func(variable2) {
    console.log(variable);
    console.log(variable2);
  }
}

let a = print(1);
a(2);
Enter fullscreen mode Exit fullscreen mode

a(2) will call function print(variable) which is going to log out variable which is 1 and then is going to log out variable2 which we passed into function func(variable2) which is a(2) . Result is :

1
2
We have an inner scope that has access to variable2 and also has access to variable from the outer scope of the print(variable). Now we add C variable:

function print(variable) {
  let c= 3;
  return function func(variable2) {
    console.log(variable);
    console.log(variable2);
    console.log(c);
  }
}

let a = print(1);
a(2);
Enter fullscreen mode Exit fullscreen mode

Result:

1
2
3

Even if the c is defined out of the function func, since JavaScript is able to read outside of inner scope, we have inner scope:

  return function func(variable2) {
    console.log(variable);
    console.log(variable2);
    console.log(c);
  }
}

Enter fullscreen mode Exit fullscreen mode

that is in outer scope:

function print(variable) {
  let c= 3;
  return function func(variable2) {
    console.log(variable);
    console.log(variable2);
    console.log(c);
  }
}

let a = print(1);
a(2);
Enter fullscreen mode Exit fullscreen mode

it is able to access variable c as well as variable in print(variable) and variabl2 which is inside the current scope.

Another Example:

let myName = "Pinkey";

function printName() {

  console.log(myName);
}

printName();
Enter fullscreen mode Exit fullscreen mode

So this variable( myName) that is external to printName(), is actually available internally inside printName(), myName is a global variable essentially and it is available inside printName().
We have the entire javaScript file that is one scope and then our function is another scope, so every scope has access to everything outside of it's scope.

Now we change the name;

let myName = "Pinkey";

function printName() {

  console.log(myName);
}

let myName="pegah"
printName();
Enter fullscreen mode Exit fullscreen mode

What is happening? it is taking the current live value of that name

let myName = "Pinkey";

function printName() {

  console.log(myName);
}

let myName="pegah";
printName();

myName= "Parwane";
printName();
Enter fullscreen mode Exit fullscreen mode

It constantly going with whatever the most recent of that value is.

Most people think of closures as functions inside of other functions:

function outerFunction(outerVariable) {

  return function innerFunction(innerVariable) {
    console.log("Outer Variable:" + outerVariable);
    console.log("Inner Variable:" + innerVariable);

  }
}

let newFunction = outerFunction("outside");
newFunction("inside");
Enter fullscreen mode Exit fullscreen mode

Here we have a function called outerFunction and that function is returning another function inside of it called inner function and then we are calling down here outer function with variable outside .
Now we are getting a new function and then lastly we are calling that new function with the variable inside.

When we fist call outerFunction we have this outervariable which we set to outside and then we have innerFunction that gets returned and the reason we are able to access this outervariable inside of **innerFunction` is closures.

outerfunction runs and outervariable is only available inside of innerfunction, now we remove this code:


newFunction("inside")

and nothing prints out and that's because we call

outerfunction here in


let newFunction=outerFunction("outside");

And it executes all of the code and then it's done executing
and outervariable is no longer accessible for example we can not log out the outervariable

console.log(outervariable);

So how the innerfunction is able to access outervariable even after it is done being executed, outervariable has gone out of scope and that is where closures come.

Innerfunction is essentially saying: I am inside outerfunction, it has outervariable so i am going to save outervariable and even the function that defined this variable is no longer available, i am still going to keep track of the outervariable.

`
function outerFunction(outerVariable) {
let outer2 = "hi";
return function innerFunction(innerVariable) {
console.log("Outer Variable:" + outerVariable);
console.log("Inner Variable:" + innerVariable);
console.log(outer2);

}
}

let newFunction = outerFunction("outside");
newFunction("inside");
`

outer2 is inside of scope but out of innerfunction, outerfunctionis contained inside innerfunction so everything in the outer function is available inside the innerfunction since in javaScript anything on the inside has access to the things on the outside of it's scope essentially it has access to it's parent scope and it's parent parent scope and so on.

All we need to know about closures is that when we have a function defined inside of another function that innerfunction has access to the variables and the scope of the outer function even if the outer function finishes executing and those variables are
no longer accessible outside that function.

Let's jump to another example:

Alt Text

securebooking() will return a new function. And what we do in this function is to update the passengerCount variable.And then let's just log the new passengerCount to the console.And now let's call the secure booking function and then store the result in a variable called Booker.

So let's analyze in detail :

Alt Text

Now, before we start running the secure booking function, our code is running in the global execution context. And in there, we currently only have this secure booking function. And so we can also say that the global scope now contains secure booking.Then when secure booking is actually executed, a new execution context is put on top of the execution stack. Each execution context has a variable environment, which contains all its local variables. In this case, it only contains the passengerCount set to zero.

Alt Text

In the next line of the secure booking function, a new function is returned and it will be stored in the Booker variable. So the global context now also contains the Booker variable. And now what else happens when the secure booking function returns? It's execution context pops off the stack and disappears. So the secure booking function has done it's job and has now finished execution.

Alt Text

Now let's see closure in action:

Alt Text

let's call booker() three times

Alt Text

How can the Booker function update this passengerCount variable that's defined in a secure booking function that actually has already finished executing?!

a closure

Now let's run the booker()

Booker() is located in the global scope. The first thing
that's gonna happen is that a new execution context is created and put on top of the call stack and the variable environment of this context is emptied simply because there are no variables declared in this function.

Alt Text

So how will the Booker function access the passengerCount variable? The secret of the closure

Any function always has access to the variable environment of the execution context in which the function was created. Now, in the case of Booker, this function was created. It was born in the execution context of secure booking, which was popped off the stack previously, So, therefore the Booker function will get access to this variable environment, which contains the passengerCount variable. And this is how the function will be able to read and manipulate thepassengerCount variable. And so it's this connection that we call closure.

Oldest comments (0)