{ Soham Kamani }

AboutBlog GithubTwitter

An introduction to Javascript closures 📦

One of the most important, but often misunderstood concepts in javascript is closures. Understanding closures unlocks programming patterns that would be difficult to use otherwise. In this post, we will understand what closures are, and go through a few examples to solidify our understanding of them.

The lifecycle of a function

First, let’s take a look at a simple function:

function getValue(){
  var a = 1
  var b = 2
  return a + b
}

var val = getValue()
console.log(val)
// Output: 3

The getValue function instantiates two variables (a and b), and returns their sum. This return value is then stored in the val variable and then printed.

simple-fn

Take a moment to think about the fate of a and b after the getValue function returns.

These variables are only visible within the function scope, which means you can’t access a or b outside the body of the getValue function. This means that once the getValue function returns, there is no way to get the values of a and b, and they can be garbage collected.

Closures in action

Now, let’s look at a different function:

function newCounter(){
  var count = 0

  return function(){
    count += 1
    return count
  }
}

var counter = newCounter()

console.log(counter())
// Output: 1

console.log(counter())
// Output: 2

console.log(counter())
// Output: 3

The return value of newCounter is actually another function. This function increases the value of count (whose scope lies within the newCounter function body) and returns it.

fn-with-closure

Similar to the last example, no one can access the value of count outside the newCounter function body. But, unlike the last example the function returned by newCounter still has access to count. This doesn’t break any rules, because the body of the returned function is still within the body of newCounter.

count can, therefore, be accessed indirectly by the returned function (which in our example is assigned to the counter variable), but it is still closed off from any other code in the program that wants to access its value. The function body inside which the count variable is closed off is called a closure.

How is this different from the variables a and b in our previous example? Well, since a and b are not referenced by any other function inside getValues closure, they can be marked for garbage collection, and for all practical purposes, they cease to exist. The count variable still exists in newCounter’s closure, because it is referenced by the returned function, and so continues to exist in the systems memory after newCounter returns.

Some more examples

The best way, in my opinion, to understand closures is to see them in action. Let’s try to understand how they work, with a few more examples:

function greeting(){
  var hello = 'hello'
  var world = 'world'

  function join(){
    return hello + ' ' + world
  }

  return join()
}

console.log(greeting())
// Output: hello world

This looks very similar to our previous example, but there are no closures here! This is because unlike newCounter, the join function, which has access to the hello and world variables, is called, and exits, within the greeting function body. greeting only returns the result of calling the join function.

If we modify the function a bit to return the join function, instead of it’s result, then hello and world would still exist inside greeting’s closure:

function greeting(){
  var hello = 'hello'
  var world = 'world'

  function join(){
    return hello + ' ' + world
  }

  return join
}

var sayGreeting = greeting()

console.log(sayGreeting())
// Output: hello world

Let’s take a look at another example, where we use an objects method to get a return value:

function Counter(){
  this.count = 0
  this.getCount = function(){
    this.count += 1
    return this.count
  }
}

var counter = new Counter()

console.log(counter.getCount())
// Output: 1

console.log(counter.getCount())
// Output: 2

Again, it may seem like this is doing the same thing as our previous counterexample, but the key difference here, is that this.count is not closed off inside Counters closure. We can still access it if we run:

console.log(counter.count)
// Output: 2

If we modify the code to make count a variable instead of an objects attribute, then it’s scope would be limited to Counters closure:

function Counter(){
  var count = 0
  this.getCount = function(){
    count += 1
    return count
  }
}

var counter = new Counter()

console.log(counter.getCount())
// Output: 1

console.log(counter.getCount())
// Output: 2

console.log(counter.count)
// Output: undefined

Closures allow you to implement private variables in javascript. They also help with the functional aspect of javascript, because without them, it would be impossible to return functions that need access to variables defined at the time the function was declared.


Like what I write? Join my mailing list, and I'll let you know whenever I write another post

Comments

Soham Kamani

Written by Soham Kamani, an author,and a full-stack developer who has extensive experience in the JavaScript ecosystem, and building large scale applications in Go. He is an open source enthusiast and an avid blogger. You should follow him on Twitter