Closures in Javascript 🔥

Understand closures in few minutes

·

6 min read

Closures in Javascript 🔥

Closures in Javascript are one of those important concepts and also many struggles to get their heads around. In the following article, I will explain everything about closures and walk you through along with code examples.

What is a closure?

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

In other words, closure gives you access to the outer function's scope from the inner function. i.e. access to the variables and functions which are present in its outer scope. So even when this function is executed in some other scope then also it remembers it outer scope which was present originally.

code example of lexical scope:

function outer() {
  var x = 10
  function inner() {
    console.log("x: " + x)
  }
  inner()
}
outer()

In the above code, outer() creates a local variable called x and function called inner(). The inner() is defined inside the outer() and available locally. Also the variable x available inside inner() only.

So the above code will give output as below:

x: 10

So the above code was an example of how lexical scope works.

code example of Closure:

function outer() {
  var x = 10
  function inner() {
    console.log(x)
  }
  return inner
}

let init = outer()
init() //  10

This code is as similar as the above code, the difference is that here we are returning inner, while In the above we were invoking inner().

tenor.gif

Let's see what's happening!

  • Here, when outer() is invoked it creates variable x and assigns it the value 10.
  • In the next line there is a function declaration, so nothing to execute.
  • return inner will return the entire inner function.
  • Execution of outer() is done here.

  • Important point to note is that all variables inside the function have a life span until the function execution.

  • That means variable x can be accessed until outer() is executing.

Then how does it giving output 10 when init() is invoked?

This is where closure comes into the picture!!

  • The inner function can refer to the variables and function of its outer function i.e. from outer function.
  • In other words, the inner function preserves the scope chain of the enclosing function at the time the enclosing function was executed and thus can access the enclosing function’s variables.

Every closure has three scopes.

  1. Local Scope (Own scope)
  2. Outer Functions Scope
  3. Global Scope

Let's see by example:

function outer() {
  var x = 10
  function inner() {
    console.log(x)
  }
  return inner
}

let init = outer()
console.dir(init)

image.png

  • Do console.dir(init) and see the scopes of function.
  • Inner function has three scopes.

    1. Inner function itself
    2. Outer function
    3. Global

What if we take parameters in function? Does it work the same?

function outer(a) {
  function inner(b) {
    console.log(a + b)
  }
  return inner
}

var init = outer(3)

init(2) // 5
  • So the answer is yes, it will still work because inner will have access to variable a.

image.png

function outer(a) {
  function inner(b) {
    console.log(a + b)
  }
  return inner
}

outer(3)(2) // 5
  • We can also invoke a function like this. Both the ways are the same.

Advantages of closures

  1. Data Hiding & Encapsulation
  2. Function Currying
  3. Module pattern

I am not going to cover all of these, I will cover Data Hiding & Encapsulation only.

What is Data Hiding and Encapsulation?

Let say we have some variable and we want to have some data privacy over it. i.e. other functions can not have access to that variable that is what data hiding is.

Let's have an example :

var counter = 0;

function incrementCounter() {
    counter++;
}
  • In the above code, We have a variable counter which can be increased by function incrementCounter.
  • But this variable counter is accessible for everyone. So there is no data privacy here.
  • So here comes data hiding, we want that nobody can access this variable counter.
  • So we will form closure around variable counter to ensure that it can not access by other functions.

Let's see code example:

function count() {
  var counter = 0

  return function incrementCounter() {
    counter++
    console.log(counter)
  }
}

var incrmentCount = count()
incrmentCount() // 1
incrmentCount() // 2
incrmentCount() // 3
incrmentCount() // 4
  • Here we wrapped variable counter inside count function and returning incrementCounter function.
  • So when count() is invoked, it creates a new copy of variable counter.
  • Now, when incrmentCount() is invoked it increments count each time it gets executed.
  • In the above example, We are invoking incrmentCount() four times, so the value of variable counter will be 4.

Point to note down here is that whenever we invoke count() and store in new variable, it creates a new copy variable counter for each variable.

Code example:

function count() {
  var counter = 0

  return function incrementCounter() {
    counter++
    console.log(counter)
  }
}

var counter1 = count() // Counter 1
var counter2 = count() // Counter 2
counter1() // 1
counter1() // 2
counter1() // 3

counter2() // 1
counter2() // 2
  • Here are counter1 and counter2 variables and both have different copies of variable counter.
  • Try to do console.dir(counter1) and console.dir(counter2) and observe the scopes of counter1 and counter2.

Scope of counter1

image.png

Scope of counter2

image.png

Disadvantages of closures

  • When we create closures, it creates new variables every time we execute the function and those variables are not garbage collected.
  • So it may lead to memory leaks.

Though, some latest browsers have smart garbage collection mechanisms. So what it does is that suppose if we have declared variables in outer function and we are not using them in inner function so it smartly garbage collects those variables and references to only used variables.

Code example:

function outer() {
  var a = 10,
    b = 13,
    c = 20
  return function inner() {
    console.log(a, b)
  }
}

var x = outer()
x() // 10 13
  • In the above code, there are three variables in the outer function. variable a, b, c.
  • But in the inner function we are accessing only a and b variables.
  • So in the closure, it will store only a and b and c will not be stored.
  • try doing console.dir(x) in console and observe the scopes.

image.png

Last but not least a challenge for everyone. Try to do it with the use of closures.

Print 1 to 10 after each and every second i.e. 1 after 1 second, 2 after 2 seconds like that. Also note that don't use let as the initializer for the loop. Try to form closure and print. Modify below-given code.

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i)
  }, i * 1000)
}

So that was all for this blog and I will see you in the next one. Till then you can read more about closures with setTimeout, Currying, Module Pattern etc.

tenor.gif

Â