How Closures Work in JavaScript: A Guide

Lexical scoping, Scope Chain, Advantages, and Disadvantages

Ayush Verma
JavaScript in Plain English

--

A closure is a combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).

In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Lexical scoping

Consider the following example code:

function outer() {
var a = 5;
function inner(){
console.log(a);
}
inner();
}
outer(); //5

Here, outer() creates a local variable called aand a function called inner().The inner() function is an inner function that is defined inside outer() and is available only within the body of the outer() function. Note that the inner() function has no local variables of its own. However, since inner functions have access to the variables of outer functions, inner() can access the variable a declared in the parent function, outer().

Notice that the console statement within the inner() function successfully displays the value of the a variable, which is declared in its parent function. This is an example of lexical scoping, which describes how a parser resolves variable names when functions are nested. The word lexical refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.

Basically here, inner()is bundled together with its lexical environment of outer(). First, inner()will check its local scope; if it does not find a, it will go to the lexical parent. So inside inner(), it forms a closure with the variable which was part of outer’s lexical scope i.e., inner()was bound to the variables of outer.

  • When functions are returned from another function, they still maintain their lexical scope i.e., they remember where they were actually present.
function outer() {
var a = 5;
function inner(){
console.log(a);
}
return inner;
}
var x = outer();
x(); //5
outer()() //5

Here though, outerno longer exists. Still, inner()remembers its lexical scope i.e., when we return inner(), not just the function code is returned, but a closure was returned( function along with lexical scope).

  • One important characteristic of closure is that it keeps the state of the outer variable between the multiple calls. The inner function does not contain a separate copy of the variable, it just keeps the reference of the outer variable.
function outer() {
var a = 5;
function inner(){
console.log(a);
}
a = 100;
return inner;
}
var x= outer();
x(); //100

Here also inner()will come along with its lexical scope i.e. awill not refer to the value, it will refer to its reference.

  • If we change the local variable declaration to let and if we pass an extra parameter bin outer(), in both the cases inner function still forms a closure. It is because bis also part of outer environment of inner function.
function outer(b) {
function inner(){
console.log(a,b);
}
let a = 50;
return inner;
}
var x= outer('Hello');
x(); //50 "Hello"

Closure Scope Chain

Every closure has three scopes:

  • Local Scope (Own scope)
  • Outer Functions Scope
  • Global Scope

To demonstrate, consider the following example code:

// global scope
var e = 10;
function sum(a){
return function(b){
return function(c){
// outer functions scope
return function(d){
// local scope
return a + b + c + d + e;
}
}
}
}

console.log(sum(1)(2)(3)(4)); // log 20
  • Now if we have a global variable with conflicting name i.e same variable name a is present globally and in outer function. Then in this case inner aor the other function scope a value will be returned. And the global ais completely new variable in the global scope. So both the a variables are completely different. Here a is present in the outer environment, if it was not present then it will global a .
var a = 10;
function sum(){
var a = 1;
return function(b){
return function(c){
return a + b + c;
}
}
}
console.log(sum()(2)(3));
//6 (1+2+3)
var a = 10;
function sum(){
//var a = 1;
return function(b){
return function(c){
return a + b + c;
}
}
}
console.log(sum()(2)(3));
//15 (10+2+3)

Advantages of Closures

1) Using Private Variables and Methods

Languages such as Java provide the ability to declare methods private, meaning that they can only be called by other methods in the same class. JavaScript does not provide a native way of doing this, but it is possible to emulate private variables and methods using closures.

2) Data Hiding and Encapsulation

Closures make data hiding possible by creating private variables i.e other functions or pieces of code will not have access to that particular data. We can say we can encapsulate the data so that other part of program cannot access it. Lets understand this with help of a example:

function counter(){
let count = 0;

return function incrementCounter(){
count++;
console.log(count);
}
}
console.log(count); // Uncaught ReferenceError: count is not definedvar counter1 = counter();
counter1(); //1
counter1(); //2
var counter2 = counter();
counter2(); //1
counter2(); //2
counter2(); //3

Here, count variable is private or hidden and will not be accessible outside the counter function. counter1 and counter2 are altogether different variables with different scopes.

Now if we want to add decrement counter also then good and scalable way to do is to create function constructors.

function Counter(){
let count = 0;

this.incrementCounter = function(){
count++;
console.log(count);
}
this.decrementCounter = function(){
count--;
console.log(count);
}
}
var counter1 = new Counter(); counter1.incrementCounter(); //1
counter1.incrementCounter(); //2
counter1.decrementCounter(); //1

3) Function Currying

Closure makes currying possible in JavaScript. Currying is an advanced technique of working with functions. Currying is a transformation of functions that translates a function from callable as f(a, b, c) into callable as f(a)(b)(c). It is when you break down a function that takes multiple arguments into a series of functions that each take only one argument.

function add (a, b) {
return a + b;
}
add(3, 4); // returns 7

This is a function that takes two arguments, a and b, and returns their sum. We will now curry this function:

function add (a) {
return function (b) {
return a + b;
}
}

This is a function that takes one argument, a, and returns a function that takes another argument, b, and that function returns its sum.

add(3)(4); //7
var add3 = add(3);
add3(4); //7

The first statement returns 7, like the add(3, 4) statement. The second statement defines a new function called add3 that will add 3 to its argument. This is what some people may call a closure. The third statement uses the add3 operation to add 3 to 4, again producing 7 as a result.

For more detail refer — JavaScript Currying: Comprehensive Guide

4) Function Factories

One powerful use of closures is to use the outer function as a factory for creating functions that are somehow related.

function Job(title) {
return function(prefix) {
return prefix + ' ' + title;
};
}
var sales = Job('Salesman');
var manager = Job('Manager');
alert(sales('Top')); // Top Salesman
alert(manager('Assistant to the Regional')); // Assistant to the Regional Manager
alert(manager('Regional')); // Regional Manager

Using closures as function factories is a great way to keep your JavaScript DRY. Five powerful lines of code allow us to create any number of functions with similar, yet unique purposes.

5) Run function only once

Functions remember how many times the function has been run by forming a closure.

var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
console.log("do something");
}
};
})();
something(); // "do something" happens
something(); // nothing happens

6) setTimeout

It is a method that calls a function or evaluates an expression after a specified number of milliseconds.

function x(){
var i = 1;
setTimeout(function(){
console.log(i)
},3000)
console.log(“After setTimeout”)
}
x();
//After setTimeout
// 1 (after 3 sec)

Here, function in setTimeout forms a closure. So this function remembers the reference to “i”. So wherever this function goes it takes the value of “i” with it. setTimeout takes a callback function and stores it in someplace and attaches a timer to that. Once the timer expires, it will take the function and put it to the current call stack, and runs it.

7) Memoization

Memoization is the programmatic practice of making long recursive/iterative functions run much faster. Closures are used here as well. Here’s an example of a basic memoize function:

8) Closures are also used to maintain state in the async world, in iterators, and in module design patterns.

Disadvantages of Closures

  • Closures prevent variables inside functions from being released by memory i.e. as long as the closure is active, the memory can’t be garbage collected. These variables will occupy memory and consume a lot of memory, which may lead to memory leakage. The solution to this problem is to delete all unnecessary local variables in time when these variables are not used i.e., set closure to null.
  • Creating a function inside a function leads to duplicity in memory and causes the slowing down of the application. The solution to this problem is to use closures only when you need privacy. Otherwise, use module patterns to create new objects with shared methods.

Conclusion

Hope that this makes you feel a bit more comfortable with the concept of Closures in JavaScript.

Thank you for reading :)

Learn more:

More content at plainenglish.io

--

--