Closure in JavaScript
Learn via video courses
Overview
Closures are one of the key concepts in Javascript and functional programming. Most Javascript developers have used closures either knowingly or unknowingly. Knowing how closure works internally will help us better predict the outcome and identify any issues in our code. In this article, we’ll understand in detail about Closure in Javascript.
The Scope
Scope in javascript refers to the current context of code, and it determines which variables and functions are accessible to that piece of code. Consider the below example :
Explanation :
Here, since the variable name is declared inside the function, its scope is limited to that function only, and it cannot be accessed outside of that function. So, if you try to access name outside the function, you’ll get the error ReferenceError: num is not defined.
Now, instead of declaring the variable inside the function, if we define name globally, then it can be accessed throughout the file.
Thus, the scope is an important concept in JavaScript that determines which variables and functions are accessible to a given piece of code and helps to prevent naming collisions, and maintains the modularity of our code.
Scopes Nesting
Before learning about the closure in javascript, we need to understand scope nesting. Let’s consider the following example :
Explanation :
In this code, outerFunction() has its scope, and within its scope, we have the outerValue variable and a nested function innerFunction() Now, due to scope nesting, the innerFunction() can access the outerValue variable which is defined in the outer function. However, the outerFunction() does not have access to the variable defined inside the innerFunction() Thus, scope nesting in JavaScript refers to the hierarchical nature of scope, where child scopes have access to the variables and functions of their parent scopes, but not vice versa.
The Lexical Scope
From the above example, how does Javascript understand that the outerValue variable inside innerFunction() corresponds to the outerValue variable in outerFunction() ?
Javascript implements a scoping mechanism named Lexical scoping. Lexical scope in JavaScript refers to how the scope of a function or variable is determined by its position in the source code. Thus, lexical scoping means that child scopes can access values inside their parent scope, but the other way around does not work.
The Closure
Lexical scope enables outer functions to access values inside inner functions. Now, instead of calling innerFunction() from inside outerFunction(), let’s return innerFunction() from outerFunction() and assign the returned function to a new variable myInnerFunction.
Now, we can call the innerFunction() from anywhere in our file, and running this code still prints Outer value in the console. It might seem confusing at once as to why does this code still work? How does Javascript still know the value of the outerValue variable when outerFunction() has already been executed? The answer to all this is - Closure.
A closure in javascript is a combination of a function bundled together with references to its lexical environment(surrounding state). This lexical environment holds the references of variables defined in the function and it also has a reference to its outer function or global environment. Every time we create a function in Javascript, a corresponding closure is created.
Thus, in the above example, innerFunction() still has reference to the outerValue variable. So, irrespective of where the function is called in the code, we can still access the value of the variable.
Practical Closures
Let’s look at a practical example where we need to use closure in javascript. Suppose, we want to maintain the count of how many times the user has clicked on a button in the UI. What do you think the solution would look like ?
The first thing that comes to mind is to create a variable count and create another method incrementCount() which we can call every time the user clicks on the button. The code would look something like this :
Well, this is a perfectly valid solution, but the problem is that the variable count is declared globally. And so, any function in our code can modify it. An ideal solution would be to restrict other functions from modifying it. So, we can encapsulate it inside a function, but then, the function updating it needs to be nested inside the function where it is declared. So, our solution looks something like this :
Thus, the count variable cannot be accessed outside the function and we can call the updateCount() which will increment the count and return its latest value.
Emulating Private Methods with Closures
In JavaScript, closures can be used to emulate private methods, which are functions that can only be called from within an object and are not accessible from outside the object. This can be useful for implementing encapsulation, which is the process of hiding the internal details of an object and exposing only its public interface.
Let's understand this with the help of an example :
The above code snippet illustrates how we can use closures to create private variables and methods. Here, we create a counter function that returns these functions - getCount(), incrementCount(), and decrementCount() which can be accessed outside the function since they are returned by it. And thanks to closures, the three public functions can share the variables and functions as they are part of the same lexical environment. However, the variable count and function updateCountBy() cannot be accessed outside the function.
Closure Scope Chain
A closure in javascript has access to three scopes :
- Local Scope (Own scope)
- Outer Functions Scope
- Global Scope
Consider this example :
Explanation :
Here, the function func3() has access to variable var3 defined in its local scope. It also has access to variable var2 defined in its parent function func2() and variable var1 defined in func1() which is the parent of its parent function. At the same time, it can also access the variable globalVar in the global scope.
Thus, the closure scope chain is the hierarchy of closures created when a function is defined inside another function, which is itself defined inside another function, and so on. Each inner function has access to the variables and parameters of the outer functions in the scope chain, which allows it to retain access to these variables even after the outer functions have returned. This can be a powerful tool for creating functions that have access to shared states and can be used in a modular and reusable way.
Dynamic Scope vs. Static Scope
Static scoping means that the value of a variable is determined at compile time i.e. before the program is executed and it cannot be changed during runtime.
On the other hand, Dynamic scoping means that the value of the variable is determined at runtime i.e. during the execution of the program. Thus, the value of the variable can change during runtime depending on the sequence of function calls.
Let’s look at an example to understand this better :
Explanation :
If this program is executed in a language that uses static scoping, the value of the num variable will always be the value of the global num variable irrespective of where it is called from. But, if we execute this code in a dynamically scoped environment, and if we call fun1() directly, then it will use the value of the global num variable, but if we call fun2() which in turn calls fun1(), then it will log the value of num declared in fun2()
Thus, static scoping is more predictable and easier to understand compared to dynamic scoping since the value of a variable is determined at compile time and is therefore fixed and remains unchanged. However, dynamic scoping can be useful in certain circumstances, such as when a variable needs to be accessed by multiple functions in a program and its value may need to be changed during the execution of the program.
JavaScript Closures in a Loop
In this section, we'll look at a common problem when using a closure in javascript. Let’s take a look at this code snippet :
Here, we’re using a for loop to iterate over the setTimeout() method thrice (when i = 0, 1, 2). So the expected output of this piece of code should be :
So, we’d expect the value of i to be printed after the i number of seconds has passed. But, the actual output is :
What we see is that the number 3 is printed after the i number of seconds. The reason for this is when we declare a variable using the var keyword, it has function-level scope. Since closure in javascript maintains the reference to the variable and not the value of the variable, so, after the for loop completes, the value of i is 3, and so we see the 3 printed after every timeout completes.
1. Using the IIFE Solution
One way to fix this problem is to use IIFE or Immediately Invoked Function Expression. In JavaScript, an IIFE is a function that is defined and immediately invoked, without being stored in a variable or assigned to a property of an object. An IIFE is often used to create a new scope in which variables can be defined and used without interfering with the global scope.
So, instead of calling setTimeout() directly, we enclose it inside a function and pass i as an argument to the function. So, we have a new reference of i each time the function is executed, and so, we get the expected result.
2. Using let Keyword in ES6
Another way to fix this problem with javascript closure is to use the let keyword to declare the variable. Since variables declared with let have a block-level scope, a new reference is stored in the closure every time the block of code is executed and so, it works as expected.
Closure Examples
Callbacks and Event Handlers
The most commonly used example of closure in javascript is in event handlers and callbacks. When defining event handlers or callbacks, we can access variables from the enclosed or global scope because the reference to these variables is stored in the closure
Function Currying
Function currying is an important concept in functional programming in which a function with partial arguments returns a new function that can be called with the remaining arguments. Thus, the functions can be partially applied or “curried” to create a new function with more specialized behavior.
Consider a simple add() function which adds the two arguments supplied to it, and returns the result
However, using the principle of function currying, we can write this function as :
Here, we can access the value of x inside the child function because of closure in javascript.
Conclusion
- Lexical scoping in Javascript means that child scopes can access values inside their parent scope, but the other way around does not work.
- Static scoping means that the value of a variable is determined at compile time whereas, in dynamic scoping, the value of the variable is determined at run time.
- A closure in JavaScript combines a function and the lexical environment within which that function was declared.
- In other words, a closure in Javascript is a function that has access to its outer scope and can use its variables even after the enclosing function is terminated.
- Closures enable us to maintain the state of a variable and use it anywhere in our code.
- Every time we create a function in Javascript, a corresponding closure is created.
- The implementation of callbacks and event handlers is based on closures in javascript.