Overview of scopes and closures
In the Understanding the difference between let and var section, we discussed scope and talked about how var
is available in the global scope, as well as in the function scope. In this section, we will be moving into scope and closures in a little more depth.
Scope
To understand scope, let's start with the following code:
let food = "sandwich" function data() { }
The food
variables and data
function are both assigned to the global scope; hence, they are termed a global variable and global function. These global variables and functions are always accessible to every other scope and program in the JavaScript file.
The local scope can further be grouped as follows:
- Function scope
- Block scope
Function scope is only available within a function. That is, all variables and functions created within a function scope are not accessible outside the function, and only exist when the function scope is available, for example:
function func_scope(){ // function scope exist here }
The block scope exists in specific contexts only. For instance, it can exist within a curly brace, { }
, along with the if
statement, for
loop, and while
loop. Two more examples are presented in the following code snippets:
if(true){ // if block scope }
In the preceding if
statement, you can see that the block scope only exists inside the curly braces, and all variables declared inside the if
statement are local to it. Another example is a for
loop, as shown in the following code snippet:
for(let i=0; i< 5; i++){ //for loop's block scope }
The block scope also exists inside the curly braces of a for...
loop. Here, you have access to the i
counter, and any variables declared inside cannot be accessed outside the block.
Next, let's understand the concept of closures.
Closure
Closure makes use of the idea of scope within functions. Remember we agreed that the variables declared within a function scope are not accessible outside the function scope. Closure gives us the ability to make use of these private properties (or variables).
Let's say we want to create a program that will always add the values 2
and 1
to an estimate
variable representing a population estimate. One way to do this is shown in the following code:
let estimate = 6000; function add_1() { Â Â Â Â return estimate + 1 } function add_2() { Â Â Â Â return estimate + 2; } console.log(add_1()) // 60001 console.log(add_2()) // 60002
There's nothing wrong with the preceding code, but as the code base becomes very big, we might lose track of the estimate
value, perhaps a function along the line to update the value, and we may also want to make the global scope clean by making the global estimate
variable a local variable.
Hence, we can create a function scope to do this for us and ultimately, clean the global scope. Here is an example in the following code snippet:
function calc_estimate(value) { Â Â let estimate = value; Â Â function add_2() { Â Â Â Â console.log('add two', estimate + 2); Â Â } Â Â function add_1() { Â Â Â Â console.log('add one', estimate + 1) Â Â } Â Â add_2(); Â Â add_1(); } calc_estimate(6000) //output: add two 60002 , add one 60001
The preceding code snippet is similar to the first one we defined, with just a tiny difference, that is, the function accepts the estimate
value and then creates the add_2
and add_1
functions inside the calc_estimate
function.
A better way to showcase closure using the preceding code is to have the ability to update the estimate value whenever we want and not at the instance where the function is called. Let's see an example of this:
function calc_estimate(value) { Â Â let estimate = value; Â Â function add_2() { Â Â Â Â estimate += 2 Â Â Â Â console.log('add 2 to estimate', estimate); Â Â } Â Â return add_2; } let add_2 = calc_estimate(50); // we have the choice to add two to the value at any time in our code add_2() // add 2 to estimate 52 add_2() // add 2 to estimate 54 add_2() // add 2 to estimate 56
In the preceding code snippet, the inner function, add_2
, will add the value 2
to the estimate
variable, thereby changing the value. calc_estimate
is called and assigned to a variable, add_2
. With this, whenever we call add_2
, we update the estimated value by 2
.
We update the add_2
function inside calc_estimate
to accept a value that can be used to update the estimate
value:
function calc_estimate(value){ Â Â let estimate = value; Â Â function add_2(value2){ Â Â Â Â estimate +=value2 Â Â Â Â console.log('add 2 to estimate', estimate); Â Â } Â Â return add_2; } let add_2 = calc_estimate(50); // we have the choice to add two to the value at any time in our code add_2(2) // add 2 to estimate 52 add_2(4) // add 2 to estimate 56 add_2(1) // add 2 to estimate 5
Now that you've learned about scopes and closures, we will move to arrays, objects, and string methods in the following section.
Further reading
To go into closures in greater detail, check out the book Mastering JavaScript, by Ved Antani.