2. INTRODUCTION
JavaScript possesses many hidden features that can often make us feel confused
and bewildered. Let’s demystify two of them.
1. Closures
2. Hoisting
3. Closures
We encounter closures several times while coding in JavaScript. So it’s quite necessary to
understand what they actually signify. Let’s first zip through some formal definitions.
What is a closure?
• A closure is a function that has access to the parent scope, even after the scope
has returned we can also define it as:
• A closure is the combination of a function and the lexical environment within which that
function was declared.
4. Scope
The Scope is essentially the lifespan of a variable in JavaScript. Where a variable is
defined plays a large role in how long that variable is around, and which functions in
your program have access to it.
So it would be safe to say that any function can be considered a closure because a
function can refer to or have access to:
• Any variable or parameter in its own function scope.
• Any variable or parameters of its outer (parent) functions.
• Any variable from the global scope.
5. Definition of closure into
3 parts.
1.We can refer to variables defined outside of
the current function.
Here, the printData() function refers to
the type variable and the model parameter of
the enclosing function setData(). So
when setData() is called, printData()uses the
variables and parameters of the former to
print the output “Audi is a Car”.
6. Inner Functions
2.Inner functions can refer to variables defined in
outer functions even after the latter have returned.
The code may seem similar to the one in the first
point. But here printData() is returned inside the
outer setData() function, instead of being immediately
called. So, the value of currentData is the
inner printData() function.
7. 3. Inner functions store their outer
function’s variables by reference, not
by value
Here, carData() returns an object
containing two closures,get() and set(),
and they both refer to the outer
variable Car. While get()obtains the
current value of Car, set()updates it.
When myCar.get() is called for the second
time, it prints the updated value of Car –
“Mercedes” – rather than the previous
value “Audi”.
8. Closure Features
Closures have another very interesting features which is that the variables in
a closure are automatically hidden. Closures store data in their enclosed
variables without providing direct access to them. The only way to alter
those variables is by providing access to them indirectly. For example, in the
last code snippet, we saw that we can modify the variable Car only obliquely
by using the get() and set() closures.
9. Loops
Looping is something that tends to take a toll on everyone. When loops are
not written correctly, we are left with weird outputs. Let’s look at one such
example where a loop produces a confusing result.
Running the code snippet should give us the output as:
0 1 2
But instead our output will be:
3 3 3
JavaScript is single-threaded so it keeps an event queue where it queues up
things to do. The closure created in each loop iteration is queued to run as
soon as the rest of the current execution context finishes and CPU time is
returned to the event loop. setTimeout here serves to simply defer the
execution of each closure until after the loop finishes running. By that point,
the final value of i is ‘3’.
10. Solution to over come the problem
We can use closures to overcome this problem. In the following code
snippet, we make use of a closure by defining nested functions.
The code prints the desired output, which is:
0 1 2
If you already have a loop that you don’t want to convert to use an
iterator function, all you have to do is wrap your closure inside
another closure in which you define new variables which capture
the current value of the variables that change on each iteration.
The trick to capturing the variables is to make sure your outer
closure executes immediately during the current iteration of the
loop. Now the above code snippet will produce the desired result!
11. Hoisting
Hoisting is employed to explain how Variables and Function declarations are ‘lifted’ to the top of a
function or a global scope. A JavaScript interpreter performs many things behind the scene, and one
of them is called hoisting.
The best way of thinking about the behavior of JavaScript variables is to always visualize them as
consisting of two parts: a declaration and an assignment.
var state; // variable declaration
state = "ready"; // variable definition (assignment)
var state = "ready"; // declaration plus definition
12. Declarations & Definition
To understand why the concept of function declarations and definitions
getting pushed to the top and assignments staying is important, let’s
consider a simple example.
• At first look, this piece of code makes no sense, for the simple reason
that not only is the variable state being print even before it is declared
but also the function hoisting()is being called before it is even defined.
Normally Line 1 should throw an error since state as it is not even
declared yet.
• Also, why is the statement at Line 9 printing undefined even though the
variable stateis already declared at Line 3?
13. Types of execution context
There are two major types of execution contexts in JavaScript —Global execution context and Function
execution context.
Since JavaScript is based on a single-threaded execution model, only one piece of code can be
executed at a time. For our code, the process is as follows :
While the execution context keeps
track of the execution of the code,
Lexical environment keeps track of the
mapping of identifiers to specific
variables.
14. Lexical environment
• Lexical environment basically is an internal implementation of the JavaScript scoping mechanism.
• Generally, the lexical environment is associated with a specific structure of the JavaScript code. For
example, a function or a block of code like a forloop. Whenever a function is created, a reference to
the lexical environment in which it was created is passed internal property named [[Environment]].
• So in our code example, the first time we print the value of the state, it does not throw an error
because internally JavaScript has already assigned an identifier to it, which is not assigned any
value. So it prints undefined The second time it is called, it again prints undefined because now it is
in the hoisting()execution context and not in the global execution context. In this context, the
variable state is still undefined.
15. Change of Code
• Now let’s change our preceding code a bit.
This time we print the value of the
variable state before hoisting() .
• This time, our code prints Ready instead
of undefined. As we learned in the earlier
section, this happens because now we are
in the global execution context and not in
Hoisting’s function execution context.
16. Things to remember
• Both function and variable declarations are hoisted to the top of the
containing scope, before any part of our code is executed.
• Functions are hoisted first, and then variables.
• Function declarations have priority over variable declarations, but not
over variable assignments.
17. Conclusion
JavaScript developers often face these issues, but fail to grasp the
reason behind the unexpected behavior. Through this presentation, we
tried to clear some of the myths and ambiguities that a developer often
comes across while dealing with Closures and Hoisting.