Closures in JavaScript Explained – Complete Guide to JavaScript Closures
A closure is an inner function referencing one or more items in its lexical scope (definition area).
Note: An inner function is a function defined inside a block, module, or another function.
In other words, a closure is a function that retains access to its lexical scope’s features even after its execution closes.
For instance, consider the following code:
showName()
is the closure in the example above because:
- It is an inner function.
- It retains a reference to its lexical scope’s variable.
So, now that we know what a closure is, we can discuss how browsers will process createProfile()
’s invocation.
How Do Browsers Process a Closure’s Lexical Scope’s Invocation?
Below is a walk-through of how JavaScript will process the createProfile()
function’s invocation.
1. Allocate memory
The first thing JavaScript does when browsers invoke createProfile()
is to allocate the memory it needs to process the function’s invocation.
2. Parse the variable’s declaration
JavaScript’s second task is to analyze myName
’s declaration, following the hoisting principles.
3. Parse the function’s declaration
The computer’s third step is to analyze showName()
’s declaration, following the hoisting principles.
4. Initialize the variable
The computer initializes myName
with the "Oluwatobi"
string value, following the hoisting principles.
5. Return the invoked function’s output
The fifth thing JavaScript does is to return showName()
’s output to the createProfile()
function.
6. Return the variable’s content
JavaScript returns the variable’s value to the showName()
function’s invocation.
7. Empty the allocated memory
The computer ends the createProfile()
function’s execution and empties the allocated memory. Therefore, all the values JavaScript stored in createProfile()
’s allocated memory will vanish forever!
So, what makes closures unique? Let’s discuss this now.
What Makes Closures Unique?
No external scope can access a function’s data by default, but a closure’s scope can.
For instance, the snippet below returned an error because the global scope cannot access the createProfile()
function’s space.
You can, however, use closures to access createProfile()
’s scope from another environment.
Here’s an example:
Question: Since the firstName
variable contains only a function (showName()
), how did the showName
external function get access to createProfile()
’s variable?
Answer: The reason the firstName
variable’s function gained access to createProfile()
’s variable is because:
- The
firstName
variable’s function is a closure. - Closures retain reference to their lexical scope’s data—even after JavaScript has closed their definition space’s execution.
Let’s discuss the reasons above in more detail.
How Do Closures Retain Access to Their Lexical Scope’s Features?
Closures retain access to their lexical scope’s features by storing referenced items in-memory.
For instance, reconsider our previous example:
Here’s is how the computer will analyze the snippet above:
A slick computer trick that makes mistakes disappear
1. Allocate memory
The first thing JavaScript does when browsers invoke createProfile()
is to allocate the memory it needs to process the function’s invocation.
2. Parse the variable’s declaration
JavaScript’s second task is to analyze myName
’s declaration, following the hoisting principles.
3. Parse the function’s declaration
The computer’s third step is to analyze showName()
’s declaration, following the hoisting principles.
4. Initialize the variable
The computer initializes myName
with the "Oluwatobi"
string value, following the hoisting principles.
5. Return the function
The fifth thing JavaScript does is to return the showName
function to createProfile()
.
Note the following:
- The computer will store the returned
showName
function in thefirstName
variable since that’s where thecreateProfile()
invocator lives. - JavaScript will also store
myName
’s value in thefirstName
variable’s memory becauseshowName
referenced it. - The
firstName
variable’s copy ofshowName
andmyName
are new code instances the computer created duringcreateProfile()
’s execution. - Browsers will not run the
"return myName"
code at this stage because we did not invokeshowName
.
6. Empty the allocated memory
The computer ends the createProfile()
function’s invocation and empties the allocated memory. Therefore, all the values JavaScript stored in createProfile()
’s allocated memory will vanish forever!
TLDR
The main gist of the walk-through above is this:
-
The
const firstName = createProfile()
statement made the computer do the following:- Invoke
createProfile()
. - Store the invocation’s returned function in the
firstName
variable. - Store
myName
’s current state (whichshowName()
referenced) in thefirstName
variable’s memory.
- Invoke
Therefore, here is what will happen whenever you invoke firstName
’s function:
1. Allocate memory
The first thing JavaScript does when browsers invoke showName()
is to allocate the memory it needs to process the function’s invocation.
2. Return the variable’s content
The computer analyzes the return
statement by doing the following:
- It checks if there’s a
myName
variable definition locally within theshowName()
function’s local scope. But there’s none, so the computer moves up to check the next scope—thefirstName
variable’s environment. - JavaScript searches for
myName
in thefirstName
variable’s memory. Fortunately, it foundmyName
there! Therefore, the computer getsmyName
’s content ("Oluwatobi"
) and returns it.
3. Empty the allocated memory
The computer ends showName()
’s execution and empties the allocated memory. Therefore, all the values JavaScript stored in showName()
’s allocated memory will vanish forever!
So, now that we know how closures retain access to their lexical scope’s features, we can discuss a few more examples.
A slick computer trick that makes mistakes disappear
Example 1: Generate Prepublication Years
The snippet above consistently returns a value that is one (1) less than the previous year because:
- JavaScript also stored all the data
decreasePublicationYear()
referenced while storingdecreasePublicationYear
insideprePublicationYear
. prePublicationYear
’s copy of thepublicationYear
variable remains accessible even after the computer closesdecreasePublicationYear()
’s execution.
Example 2: Generate the Multiple of a Constant
The snippet above consistently multiplies multipleOfX
’s argument by 3
because:
- JavaScript also stored all the data
multiplyXByY()
referenced while storingmultiplyXByY()
inside themultipleOfX
variable. (In the instance above,multiplyXByY
referenced3
(parameterx
’s value)). multipleOfX
’s copy of the value3
remains accessible even after the computer closesmultiplyXByY()
’s execution.
How to Create NPM Package
Example 3: Create a Publication Year Calculator
The defaultYear()
method remembers the publicationYear
’s updated state because:
- JavaScript also stored all the data
updatePublicationYear()
referenced while storingupdatePublicationYear
’s invocator inside thepublicationYearCalculator
variable. (In the instance above,updatePublicationYear
referencedpublicationYear
). publicationYearCalculator
’s copy ofpublicationYear
remains accessible even after the computer closesupdatePublicationYear()
’s execution.- The calculation happened on
publicationYearCalculator
’s copy ofpublicationYear
—not on the one inside the anonymous function.
Note the following:
- The
publicationYearCalculator
variable’s function is an Immediately Invoked Function Expression (IIFE). - Developers use the term “module pattern” to reference the neat organization of private and public features inside an object (as we’ve done in the
publicationYearCalculator
’s IIFE above). publicationYearCalculator
’s IIFE contains two private and three public items.publicationYear
andupdatePublicationYear()
are the private features.addOne()
,substractOne()
, anddefaultYear()
are the public items because they are part of the returned object.
- Although private items are inaccessible outside their lexical scope, you can access them through the public closures—because they have access to in-memory copies of their referenced features even after JavaScript has closed the IIFE’s execution.
updatePublicationYear()
,addOne()
,substractOne()
, anddefaultYear()
are all closures because they are inner functions that reference some of their lexical scope’s features.