Closures
Open the project folder
In our starter files, open the index.html page from this lessons folder:
09.Scope-Hoisting-Closures > 06.Closures
Starter file code
When we create functions, they don’t always need to be standalone functions, they can also be nested too.
In the starter project, we have an empty file for this lesson. Let's use this to create a simple function to demonstrate the effect of closures.
Nesting functions
First, create an outer function:
function outer() {
  let outerValue = 'outer';
}
Then we can nest inside additional functions:
function outer() {
  let outerValue = 'outer';
  function inner() {
    let innerValue = 'inner';
    console.log(innerValue);
    console.log(outerValue);
  }
}
We know from earlier, that when we nest scope like this, we can still access variables created in outer scopes, so inner scope can access outer variables.
This inner function needs to be called to run:
function outer() {
  let outerValue = 'outer';
  function inner() {
    let innerValue = 'inner';
    console.log(innerValue);
    console.log(outerValue);
  }
  // 1. call inner function
  inner();
}
// 2. call outer function
console.log(outer());
Variable access
The console will print:
inner //innerValue
outer //outerValue
undefined //outer()
We have access to the innerValue and outerValue variables. Ignore the undefined as we have not yet returned anything back from the function.
This is working, we have the console logs, meaning we have access to both variables from the inner function.
We can access innerValue because it is local:
function inner() {
  let innerValue = 'inner';
  console.log(innerValue);
  console.log(outerValue);
}
And outerValue because of the scope chain:
function outer() {
  let outerValue = 'outer';
So, scope, or lexical scope, allows this inner function access to two variables.
Returning the inner function
But what about if this inner function was pulled out of it's current setting? We are not physically moving the inner function, but instead what about if we stored it into a variable and used it somewhere else?
To be able to do this, instead of calling the inner function, we can return it:
function outer() {
  let outerValue = 'outer';
  function inner() {
    let innerValue = 'inner';
    console.log(innerValue);
    console.log(outerValue);
  }
  // return inner function
  return inner;
}
console.log(outer());
The important part to understand is we are returning the inner function when we call the outer function, and we can see this inner function in the console:
ƒ inner() {
  let innerValue = 'inner';
  console.log(innerValue);
  console.log(outerValue);
}
Then store this into a variable:
function outer() {
  let outerValue = "outer";
  function inner() {
    let innerValue = "inner";
    console.log(innerValue);
    console.log(outerValue);
  }
  return inner;
}
// 1. store returned function into a const
const innerRef = outer();
// 2. log to console
console.log(innerRef);
This innerRef variable can now be used anywhere. Giving us a function reference independent of it's original outer function.
The closure
This now leaves us with a key question. Can this innerRef access variables from the outer function (outerValue)?
Let’s call the function and find out:
console.log(innerRef());
The console will reveal we can access the innerValue and outerValue variables. This may seem strange because of what we know about scope.
And this all relates to closures. A function will remember what variables it had access to, and this is a closure. A closure is like putting a fence around everything we have access to and remembering all these variables, wherever the function may be called in the future.
And this can be useful because now instead of needing to create outerValue as a global variable, we can restrict the access, and still allow the inner function to use it.
Also note that closures can only happen with functions.
Further example
If this is still not clear, let’s look at another example. Using a players score that needs to be updated:
let score = 0;
function updateScore() {
  score = score + 10;
  console.log(score);
}
updateScore(); // 10
If we update multiple times, it will increase multiple times:
updateScore();
updateScore();
updateScore();
Console:
10
20
30
This is fine and works as required, but score is a global variable. It can be updated from anywhere else. It may be better to have more control over this variable, and only update it in the function we created for this purpose.
Restricting variable access
You may think let’s move the variable into the function:
function updateScore() {
  let score = 0;
  score = score + 10;
  console.log(score);
}
updateScore();
updateScore();
updateScore();
This creates a function scope, and score cannot be accessed from outside. This solves one problem but creates another.
The console now outputs the same values:
10
10
10
This happens because the variable resets back to zero at the beginning of each function call.
Also, if this was a real game, we would probably want access to the score variable in other areas, such as to display to the user. To help with this, we can make use of a closure. This will nest a function to update the score variable:
function updateScore() {
  let score = 0;
  function increase() {
    score = score + 10;
    console.log(score);
  }
}
Return this inner function as before:
function updateScore() {
  let score = 0;
  function increase() {
    score = score + 10;
    console.log(score);
  }
  return increase; // return inner function
}
And store this function into a new variable:
function updateScore() {
  let score = 0;
  function increase() {
    score = score + 10;
    console.log(score);
  }
  return increase;
}
// 1. store return value into const
const newScore = updateScore();
// the function can again be called multiple times, if required
newScore();
newScore();
newScore()
The console values are now reinstated:
10
20
30
This solves the problems we had before.
With the original updateScore function, we reset the score variable back to zero. This meant that each time we called it, the result was always 10. Using a closure solves this by remembering the score variable in memory each time it is called.
The score variable now also has a narrower scope, so it cannot be updated accidentally elsewhere.
Finally, we can still access the score outside the function, by storing it into a variable. Meaning we can show the score to the player at any time.