Closures In JavaScript

Introduction

Closure in Javascript can be defined as the bundle of functions along with its lexical environment. Closures in Javascript are created every time a function is created.

What are Scopes?

Scopes in JavaScript are the portion of the code where the variable is accessible.

const name = "abc";
console.log(name) // abc
if (true) {
    let personName = "xyz"; // same for const as well
    console.log(personName);
}
console.log(personName); // throws Reference Error

When we look at the above examples, we see that we had no issues accessing "name" but accessing "xyz" was declared inside the scope of if statement. So, this defines the definition of scope which we discussed earlier. In JavaScript, we have different types of scopes, some of them are:

  1. Global Scope

  2. Block Scope

  3. Functional Scope

Global Scope

As the name suggests, the variable is accessible everywhere in the program is said to exist in the global scope.

let name = "abc"
const mainFunction = () => {
console.log(name);
}
mainFunction(); // abc
console.log(name); // abc

Here, the variable name is accessible inside the main function as well outside it. The name can be accessed from anywhere in the code. So the name exists in the global scope.

Function Scope or Local Scope

When a variable is declared inside the function, it can only be accessed from within that function. This means that this variable exists in the functional scope and cannot be used outside this scope.

const main = () => {
    const newName = 'abc'; // this is defined in function scope
    console.log(newName);
}
main();
console.log(newName); // Reference Error

Block Scope

With the introduction of let and const keywords, variables can be scoped to the nearest block of code or pair of curly braces. Just for the information, var is not block scoped.

{
    let name1 = "abc";
    console.log(name1);
}
{
    var newName = "xyz";
    console.log(newName);
}
console.log(name1); // Reference Error
console.log(newName); // xyz

But the most important scope in terms of understanding closures is Lexical scope. But before jumping directly to lexical scopes we should have some knowledge regarding Scopes Nesting.

Scopes Nesting

{
    let name = "abc";
    {
        console.log(name);
        let newName = "xyz";
        {
            console.log(newName);
            console.log(name);
        }
    };
console.log(newName); // throws ReferenceError
};

Here we have multiple variables defined in multiple nested scopes and this code runs perfectly fine. So we can easily conclude from here that the variable from the outer scope is accessible to the inner scope.

function demoFunction() {
    let name = "abc";
    function newFunction() {
        console.log(name);
    }
    newFunction();
}
demoFunction();

Here, name is accessible in newFunction and when newFunction is called it logs the name.

Lexical Scopes

We saw that the variable of the outer scope is easily accessible to the inner scope. This is possible because of the lexical scoping. In the above example, the lexical scope of the newFunction consists of demoFunction and global scope. In easy terms, lexical scoping means that inside an inner scope, you can use the variable of the outer scope. It is called lexical scope as the engine determines the scopes during the lexical time.

Closures

A closure is the combination of a function bundled together with references to its surrounding state (the lexical environment). As we know what lexical scoping is, this definition is explanatory. But we will define closures in JavaScript as simply as possible.

function outerFunction(number) {
    let initial = 1;
    function innerFunction(newNumber) {
        initial = initial * newNumber + number;
        return initial;
    }
    return innerFunction;
}
let returnedFunction = outerFunction(2);
console.log(returnedFunction(1));
console.log(returnedFunction(3));

Here, we have an outerFunction, which has an innerFunction. This innerFunction is manipulating the initial which is defined in its outer scope. Now, when we call outerFunction, it returns innerFunction and when we try to call innerFunction, we see that it still has access to an initial, though the function is called outside of the scope of outerFunction. This is possible because of closures.

Because of closures, functions in JavaScript can close over the value of their outer scope and remember them. So no matter from which scope they are called, they can easily access the variables in their outer scope.

A closure is a function that remembers its outer variables and can access them even when called independently

Scope Chain

In JavaScript, we know that a scope has access to its parent's scope or outer environment, which in turn has access to its outer environment. This can be represented by hierarchical chain scopes and this is called scope chain.

Closure and Scope Chain

By scope chaining, we know that JavaScript will keep looking for the variable in its scope as well as its parent scope till it reaches the global scope. The same is the case with closures.

let name = "abc";
function outerFunction() {
    let name = "xyz";
    function innerFunction() {
        let name = "def";
        return function() {
            name = "ghi";
            console.log(name);
        }
    }
    return innerFunction();
}
const function = OuterFunction();
function(); //prints "ghi"|

Here we can simply predict that the output is "ghi". Now suppose we comment on the line where we are assigning "ghi"

Now, the output will be "def". Again if you comment out line where "xyz" is assigned you will get "abc" and when abc is commented out you will get ReferenceError.

JavaScript is searching got the variable name. The search begins from the local scope where name is "ghi" and if the variable is found, Javascript stops searching and returns which is exactly what happened when we ran the code for the first time, but if no variable is found Javascript looks in the parent's scope for name and where the parent's parent and finally the global content. If the variable is not present in the global scope, ReferenceError is raised.

The scope chain or bundling or outer environment with function happens at the time of the function's creation. In simple words, the outer environment of the function is defined statically by location within the source code.

Static and Dynamic Scopes

In static scope, variables are referenced in a context at the time of creation. Whereas in dynamic scope, variables are referenced at the run time.

var first = 2;
function firstFunction() {
    var second = first + 5;
    return second;
}
function secondFunction() {
    var first = 6;
    return firstFunction();
}
function main() {
    firstFunction(); // static scope -7, dynamic scope- 7
    secondFunction(); // static scope- 7, dynamic scope: 11
    return 0;
}
main()

For the firstFunction, it will return 7 in both types of scopes. But for the secondFunction, if we have the static scope, it will return 7 and for the dynamic scope, it will return 11. The reason is, in the case of static scoping, the value of first is determined at the time of function creation which is 2. So, the output is 7. But in the case of dynamic scoping, the value of the first is determined at the runtime. When secondFunction is called, the value of first inside its scope is 6, so the output would be 11.

Difference between Static and Dynamic Scope

Static ScopeDynamic Scope
In static scoping, variable always refers from top level enviroment.In dynamic scoping, variable is searched first in the current block and then its caller function.
Most of the programming languages uses static scoping.Very few languages uses dynamic scoping.
It is more developer friendly.Not much developer friendly.
Examples of languages using static scope: JS, C++, C etc.Languages using Dynamic scoping: Perl.

Emulating private methods with closures

In JavaScript, we don't have an out-of-the-box way to create a private variable as we have in other languages like JAVA, C++. Private variables are the variables which are visible only to the class to which they belong and cannot be accessed/modified directly from outside. But we can use closures to emulate this behaviour and create a basic factory function with a private variable and setter and getter function.

Factory function is nothing but a function that returns an object. These are different from class and constructor as they don't require new keyword for instantiation.

function Friend(name, job) {
    let _name = name; //Private variables here
    return {
        getName() {
            return _name;
        }
        setName(newName) {
            _name = newName;
        }
    }
}
const name = Friend("abc");
console.log(name.getName()); // abc
console.log(name._name); // undefined
name.setName("xyz");
console.log(name.getName()); // xyz

Here we have created a simple Factory Function Friend with a private variable name, getter function getName, and setter function getName, and setter function setName. When we try to access _name directly, it returns us undefined as it is in the function scope and cannot be accessed from outside. So using closures we can emulate private variables.

JavaScript Closures in a Loop

for (var i=0, i<5, i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

The above code will print 5, 5, 5, 5, 5 Firstly, we have used var in front of i and secondly a setTimeout function is called. Now because setTimeout function calls the function after 1 second, by the time 1 sec, passes the loop has completed its iteration and the value of i is 5. As i is of var type, its value remains the same(var not blocking scoped). So when the callback function is called, i is 5 and hence we get 5, 5, 5, 5, 5 as output. To get 0, 1, 2, 3, 4 as output- there are two ways

Using IIFE and Closures

The bottleneck in the above approach was the value of i being global 5 for each callback function call. If we can somehow remove this, our problem would be solved. So, we create a new scope for i in each iteration. In this way, each function call would have its own value of i and we get the desired output:

for (var i=0; i<5; i++) {
    (function(i) {
        setTimeout(function() {
            console.log(i);
      }, 1000)
    })(i);
}

In the above code, we just wrapped the setTimeout function inside an IIFE, so now for each callback function (function inside the setTimeout) the value of i will not be the value of the iterator but the value of i in the outer function. Since this function is called for each value of i it will log all the value of i as desired.

Using let keyword in ES6

A very simple solution to this can be to use let instead of var in front of i. let creates a new lexical scope in each iteration which means that we will have a new i in each iteration.

for(let i=0; i<5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

One of the easy ways to understand this is when we use var, we have only one instance of i and at each iteration its value is changing. But when we use let, for each iteration a new instance of i is created with different value. Hence, we get the desired result.

Higher Order Functions

Higher Order Functions are the functions that either take another function as an argument or return another function or both.

const multiply = (a) => {
    return function innerFunction(b) {
        return a*b;
    }
}
const multiplyBy2 = multiply(2);
const multiplyBy3 = multiply(3);
console.log(multiplyBy2); // f innerFunction() {}
console.log(multiplyBy3); // f innerFunction() {}
console.log(multiplyBy2(7)); //14
console.log(multiplyBy3(5)); //15

In the above snippet, we have defined a function multiply which is a HOF. It takes "a" as one of its arguments and return anotherFunction i.e., innerFunction. Now we are calling multiple and storing whatever it returns in a variable multiplyBy2 and multiplyBy3. Now if we log these variables out we see that they are actually a function. This function takes another argument as an argument and we are doing exactly in later lines. Here the innerFunction can access a because of closures as it is presented in its outer scope.

Use of Closures

  1. The for-loop dilemma

  2. Emulating private variables in JavaScript

  3. Creation of Higher order functions

But still there are some advanced use cases of closures which I haven't discussed.

Currying

Currying is the pattern of functions that immediately evaluate and return other functions. This is made possible by the fact that JavaScript functions are expressions that can return other functions. Curried functions are constructed by chaining closures by defining and immediately returning their inner functions simultaneously. It is a very powerful technique and is frequently asked.

function sum(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        }
    }
}
let result = sum(4)(5)(8);
console.log(result); // 17

Here the variables a and b are accessible to the innermost function because of closures.

Function Composition

Function Composition is the ability to create functions from other functions. It is very powerful feature and can reduce the redundancy in our code to great extent.

function add(a, b) {
    return a + b;
}
// Wrapping addBy2 over add
function addBy2(num) {
    return add(2, num)
}
console.log(addBy2(4)); // 6

Examples of Closures

Example-1:

function by(propName) {
    return function(a, b) {
        return a[propName] - b[propName];
    }
}
const person1 = {name: 'abc', height: 178};
const person2 = {name: 'def', height: 182};
const person3 = {name: 'xyz', height: 176};
const array = [person1, person2, person3];
const sortedArray = array.sort(by('height'));
console.log(sortedArray);

So, we have a function by which takes params as an argument and then returns another function which takes a and b as arguments and returns the difference between a[propName] and b[propName]. Then we have some values initializzation and then we are calling this by function as a comparator in sort function. So in the by function, because of closures, propNames is available to the inner function and hence the difference between the heights is returned.

Example 2

function outer() {
    let a = 10;
    let b = 100;
    function inner() {
        let a = 20;
        console.log("a= " + a + " b= " + b );
        a++;
        b++;
    }
    return inner;
}
const x = outer();
const y = outer();
x(); // a=20, b=10
x(); // a=20, b=11
x(); // a=20, b=12
y(); // a=20, b=10

Here, the value of a and b are inside the outer function and we have created two instances of outer function i.e., X and Y. So any change which we make in the scope of X would have an effect on the scope of Y

Example 3

var result = [];
for (var i=0; i<5; i++) {
    result[i] = (function inner(x) {
        return function() {
            console.log(x);
        }
    })(i);
}
result[0](); // 0
result[1](); // 1
result[2](); // 2
result[3](); // 3
result[4](); // 4

Conclusion

Closures in JavaScript are something that is used by everyone but still, most of the developers are not familiar with it and it is completely fine.

A closure in JavaScript is a feature where an inner function has access to the outer (enclosing) function's variables - a scope chain.

Closures in JavaScript are created along with the function.

Lexical scoping is the environment that holds the variables of the current scope as well as the outer scope.

As each scope in JavaScript has access to its parent's scope or outer environment, we can nest these scopes in hierarchical order called scope chaining.

Each closure has three scopes namely global scope, function scope or local scope and block scope.

in languages using static scopes, variables are referenced at the time of creation whereas, in dynamic scoping, variables are referenced at runtime.

With the help of closures, we can solve the loop dilemma in JavaScript.

Closures in JavaScript find their uses every time a function is created. Some common use cases are Higher-order functions, currying and Function Decomposition.

Thanks for reading the article!!!