Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript Function Closures

Chapter 32 35 mins

Learning outcomes:

  1. What are closures
  2. What is a lexical environment
  3. The [[Scopes]] attribute in Chrome
  4. Retaining local environments of functions
  5. Practical applications of closures

Introduction

In the chapter, JavaScript Functions Basics, we briefly came across about the concept of a function closure. Closures are one of the most useful and beautiful ideas of functions in JavaScript and many other languages that support first class function and are based on a lexical scoping system.

Don't worry if you aren't aware of the term 'lexical scoping system'. This chapter aims to clarify all the confusion you might have related to closures by detailing every single aspect of them to the core.

We'll see what are lexical environments, how lexical scoping works, how are names inside functions resolved, the [[Scope]] internal attribute, and a ton more stuff related to closures.

Let's begin...

What are closures?

Let's start by the definition of the concept to get a headstart on what exactly is a closure.

A function along with its lexical environment is collectively called a closure.

Be definition, all functions in JavaScript form closures. It's just that some of these turn out to be more useful and more noticeable than others. We'll see all these details in the sections below.

Once again, you don't need to worry about getting every bit of this definition into your mind right now. We'll address every part of the definition one-by-one and make it 100% sure that by the end of this chapter, you really know what a closure is.

Closures aren't that easy that one could understand them without putting in any little amount of effort, and they are even not that difficult to take on years and years to understand.

In the following section, we explain one of the most fundamental ideas to closures — a lexical environment.

What is a lexical environment?

The biggest source of confusion in understanding closures is the term 'lexical environment', or simply just the word 'lexical'.

Well, it actually represents an extremely elementary concept. Let's understand it...

In computer science,

The term 'lexical' simply means 'source code' or in other words, 'relating to the text of a program'.

So does this mean that a lexical environment has to do something with the source code of a program?

Definitely yes!

Let's inspect it further...

Consider the code below:

var a = 'static';

function f1() {
   console.log(a);
}

function f2() {
   var a = 'dynamic';
   f1();
}

f2();

What do you think would be the output of this code? Think very carefully, taking as much time as you need.

Time to reveal the answer: it's 'static'.

Surprised?

Well anyone beginning to learn JavaScript, with no prior experience of working with a programming language that is also based on a lexical scoping model, should be.

Here's what you might have thought:

  1. A global variable a is declared and initialised to the value 'static'. Then two functions f1() and f2() are created.
  2. Next, f2() is called.
  3. It creates a local variable a and assigns it the value 'dynamic'.
  4. After this, f1() is called. The statement console.log(a) is encountered.
  5. Since a is 'dynamic' at this point, 'dynamic' is logged in the console.

Well, this is what would've happened if JavaScript was dynamically-scoped.

However, it's not dynamically-scoped. It's rather lexically-scoped, also known as statically-scoped.

In this scoping system, in order to resolve a name inside a function, it's first searched in the local environment of the function and then in its lexical environment.

The lexical environment for a function f simply refers to the environment enclosing that function's definition in the source code.

In other words, the lexical environment of a given function is based on the source code of the program — where the function is defined. That's why we call it the 'lexical' environment i.e environment based on the source code.

To understand what is meant by a function definition, please refer to JavaScript Function Basics.

So how exactly is it based on the source code? Like what is meant by this?

Well, the JavaScript compiler reads the program's source code and determines the environment accessible to a given function based on its definition, when compiling it.

For instance, if a function f is defined in the global scope, its lexical environment (determined when compiling the function) is simply the global environment.

All the set of environments enclosing a function are collectively referred to as its lexical environment.

The lexical environment of a function can contain multiple environments.

The lexical environment is determined once and then fixed for the entire course of the program. This is why JavaScript is called a statically-scoped language — the places where given names (of variables and functions) are accessible in a program are static (i.e. don't change) and governed by the source code.

Time to understand all this in terms of an actual example.

Let's reconsider the code snippet shown above:

var a = 'static';

function f1() {
   console.log(a);
}

function f2() {
   var a = 'dynamic';
   f1();
}

f2();

Here, the function f1 is defined in the global scope, likewise its lexical environment is the whole global environment. The same goes for the function f2().

Now when f2() is called in line 12, first a local variable a is created and initialized to 'dynamic' and then f1() is called. Inside f1(), the statement console.log(a) is encountered. At this point, the name a has to be resolved.

Here's how the resolution goes.

  1. First the local scope of f1 is searched for the name a. Trivially, because this local environment is empty, no such name is found. Consequently, searching moves to the lexical environment of f1.
  2. Searching in the lexical environment goes orderly as well. That is, first enclosing the function's first enclosing environment is searched, then the outer enclosing scope, and so on until eventually the global environment is reached, which has no further enclosing environment.
  3. The enclosing lexical environment of f1 is simply the global scope, hence searching is conducted here for the name a. Since a match is found, bound to the value 20, the name a in console.log(a) is resolved with the value 20.

This is how name resolution works in JavaScript, in Python, in PHP, and in all statically-scoped languages.

If you're lost in all these definitions, let's quickly recap what we've learnt so far in this section.

Lexical means relating to the source code, or simply based on the source code. Lexical environment for a function merely refers to the environment enclosing the function's definition. It's called lexical because it's based on the source code an remains fixed throughout the execution of a program.

When a name x is referred to in a function f, it's first searched in the local environment of f and then in its lexical environment.

The [[Scopes]] attribute in Chrome

These days, browser consoles expose quite many internal attributes of given objects for better inspection. Google Chrome, particularly, provides with a very flexible console.

According to the ECMAScript spec, every function holds an internal attribute [[Environment]] that simply contains the lexical environment of the function.

The best part is that this attribute is one of those attributes exposed by the console in Chrome. However, at the time of this writing, its naming is a bit different — it's called [[Scopes]].

This means that we could actually inspect the lexical environment of any given function f in Chrome by inspecting its [[Scopes]] attribute.

Consider the same code defined above. Note that we're not calling f2() for now, likewise the last statement is commented out. We'll call it later on, once we are done with some basic inspection:

var a = 'static';

function f1() {
   console.log(a);
}

function f2() {
   var a = 'dynamic';
   f1();
}

// f2();

Let's inspect the lexical environment of f1. Here's how to do it.

  1. First log the function using console.dir(), by providing a reference to the function.
  2. Next, click the caret icon next to the function to expand all its properties.
  3. In here, notice the [[Scope]] attribute. It's an array, likewise it could also be expanded to see the contents of the array.

Let's add the console.dir() statement in our code to inspect f1:

var a = 'static';

function f1() {
   console.log(a);
}

function f2() {
   var a = 'dynamic';
   f1();
}

// f2();
console.dir(f1);

For the code above, here's the completely expanded console output:

First the function f1 is expanded in order to see its properties and internal attributes. In here, the [[Scopes]] attribute is expanded to view the lexical environment of f1.

In this case, it is an array with just one object which points to the global environment. Finally, we expand this element of [[Scopes]]. And then follows a large list of properties, of which we've only been able to display the first five.

See the name a in here? This is the global variable a. It is what's used to resolve the name a in f1 when it is called from within f2, in the code above.

Let's review what happens when a is encountered in the function f1 above.

  1. The local environment of f1 is searched for the name a. Nothing is found here, likewise searching shifts to the lexical environment of f1.
  2. Within the lexical environment, represented via [[Scopes]], the name a is searched for in the first environment enclosing f1. In this case, that environment just turns out to be the global environment.
  3. Likewise, a is searched for in the global environment. As a match is found, bound to the value 'static', the a in f1 is resolved with 'static'.

Simple?

Functions in the same scope have the same lexical environment

One thing to keep in mind is that all the functions in the same scope point to exactly the same lexical environment. This is a trivial detail, which should've been met anyways.

This means that in the code above, the [[Scopes]] of f2 would be the same as the [[Scopes]] of f1, because they both are in the global scope. They would refer to the same internal environment object stored in memory.

Alright, moving on, let's make one simple change to the code above and then reconsider it. We'll encapsulate all of it inside an IIFE:

(function() {
   var a = 'static';

   function f1() {
      console.log(a);
   }

   function f2() {
      var a = 'dynamic';
      f1();
   }

   // f2();
   console.dir(f1);
})();

Now, let's execute this code. Here's what the console shows us:

The [[Scopes]] attribute of f1 (and f2) contains two entries. The first one is for the first environment enclosing the definition of f1 i.e. the local environment of the IIFE, while the second one is the second environment enclosing f1 i.e. the global environment.

You might be thinking: why isn't there f2 in the first entry of [[Scopes]] above? The reason f2 is not in there is because of optimizations made by the V8 engine (Chrome's JavaScript compiler). In this case, the engine sees that f2 isn't required by any of the functions (f1 and f2) inside the IIFE, likewise it isn't stored in the lexical environment of these functions.

As before, let's see what happens when the name a is encountered inside the function f1, the moment it is called:

  1. The local environment of f1 is searched for the name a. Nothing is found here, likewise searching shifts to the lexical environment of f1.
  2. Within the lexical environment, represented via [[Scopes]], the name a is searched for in the first environment enclosing f1 i.e. the local environment of the IIFE.
  3. Here, indeed a is found with the value 'static', likewise the a in f1 is resolved with 'static'.

Easy, wasn't this?

We'll consider more examples utilising inspection of [[Scopes]] throughout this chapter. For now, let's dive even deeper into how closures work.

More examples

Let's consider a very simple example extending our previous code snippet:

var a = 'easy';

(function() {
   var b = 'easy';

   function f1() {
      console.log(a, b);
   }

   function f2() {
      var a = 'difficult';
      var b = 'difficult';
      f1();
   }

   f2();
})();

As before try to reason about the output of this code prior to reading the test any further.

See the output
easy easy

Alright, let's see how does the code above get executed.

  1. A global variable a is created and assigned the value 'easy', followed by creating and invoking an IIFE.
  2. A local environment is created for this IIFE. Here a local variable b is created initiliased to the value 'easy'. Next, two functions f1 and f2 are created.
  3. During this stage, many things happen as detailed in the JavaScript Functions Basics chapter. One of them is to save the lexical environment of the function inside inside the [[Environment]] internal attribute of the function. Note that this happens before the functions are actually called.
  4. With all this done, execution moves to line 16, where f2() is called.
  5. A local environment is created for f2. Here, two local variables a and b are defined both holding the value 'difficult'.
  6. Next up, f1() is called.
  7. A local environment is created for f1. Execution moves to the statement console.log(a, b) on line 7. Now before this statement could be processed, the names a and b have to be resolved.
  8. Let's first resolve a:
    1. The local environment of f1 is searched for a. No match is found, hence searching moves to the lexical environment.
    2. Within the lexical environment, the first enclosing environment of f1, i.e. the local environment of the IIFE, is searched for b.
    3. Nothing is found even here, likewise searching moves to the second enclosing environment (within the lexical environment of f1), i.e. the global environment.
    4. Since a match a = 'easy' is found here, the name a in console.log(a, b) is resolved with the value 'easy'.
  9. Now let's resolve b:
    1. The local environment of f1 is searched for b. No match is found, hence searching moves to the lexical environment.
    2. Within the lexical environment, the first enclosing environment of f1, i.e. the local environment of the IIFE, is searched for b.
    3. Since a match b = 'easy' is found here, the name b in console.log(a, b) is resolved with the value 'easy'.
  10. Both the names a and b are resolved at this point, therefore the statement console.log(a, b) executes, completing the execution of f1, the execution of f2, and ultimately the execution of the entire program.

Simple?

Retaining local environments of functions

Frankly speaking, at this point we do know a couple of things related to how a closure works internally. In particular, we know what are lexical environments; what are free variables, how name resolution works inside functions.

But still one thing is unanswered — 'how come JavaScript is able to hold onto a local environment of a function once it exits'.

In this section, we aim to answer this question. Closures are definitely tedious to explain, but worth all the effort.

Take a look at the code below:

function f1() {
   var a = 'difficult';

   return function() {
      console.log(a);
   };
}

var a = 'easy';
var f2 = f1();

f2();

Try to reason about what output would be produced by the piece of code and why?

See the output
difficult

When the code above is executed, here's what happens:

  1. A function f1 is created followed by a global variable a assigned to the value 'easy'.
  2. Next up, the function f1() is called.
  3. A local environment is created for the function f1. In here, a variable a is defined holding the value 'difficult'. Then an anonymous function is created whose lexical environment includes this local environment (of f1), and finally this function is returned.
  4. At this point, since a value is returned from the function f1 which means that it has finished executing, the JavaScript engine has to delete its local environment. Now before the deletion, a quick search is conducted to find out whether any references exist to this environment. Since outside the local scope of the function f1, there indeed does exist a reference — in the [[Environment]] attribute of the returned function — the environment is kept on in memory. However, the other necessary procedures are performed as usual for e.g. clearing away the call stack frame for f1.
  5. The invocation f1() completes and its return value, which is an anonymous function, is assigned to the global variable f2. Then f2() is called.
  6. A local environment is created for f2. Execution moves to line 5, at which point the name a in console.log(a) ought to be resolved.
  7. Here's how it's resolved:
    1. The local environment of f1 is searched for the name a. Nothing is found here, likewise searching moves to the lexical environment.
    2. Within the lexical environment, the first enclosing environment of f1, i.e. the local environment of the function f2, is inspected for a.
    3. Here since a match a = 'difficult' is found, the name a in console.log(a) is resolved down with the value 'difficult'.

Simple?

The most interesting thing to note over here is that the local environment of f1 is NOT deleted even when f1 exits (via the return keyword). This is because a reference to its local environment exists in the returned anonymous function.

The engine checks if there is at least one reference to the local environment of a function exiting, outside the function. If there is, the local environment is not garbage-collected.

In the code shown above, you might think that no reference exists to the local environment of f1 outside the function i.e. there is only one reference to the local environment of f1 which is stored in the anonymous function, which is itself part of f1.

Hmm..

It is true that the anonymous function, which is the only thing that refers to the local environment of f1, is part of f1. But you forgot one crucial thing.

The anonymous function is being returned by f1. This means that it will ultimately be available outside f1, in the region where f1 is called (in the code above, it is called in the global scope in line 10).

And so, for the [[Environment]] attribute of the anonymous function to remain intact, the local environment of f1 has to be kept in memory.

If we were to rewrite f1 such that the return keyword was removed, its local environment would no longer be held onto once it exits.

function f1() {
   var a = 'difficult';

   (function() {
      console.log(a);
   });
}

var a = 'easy';
var f2 = f1();

f2();
Do you recall why do we need to enclose the function above within a pair of parentheses (()). If you don't, you could learn more about it at JavaScript Functions — Basics.

Why? Simply because the local environment is not needed anymore outside the function.

While f1 is being executed and its local environment is in memory, the [[Environment]] attribute of the anonymous function within it contains a reference to f1's local environment.

But when f1 exits, the anonymous isn't needed anymore, likewise it's garbage-collected and along with it the attached reference to the local environment of f1. With this reference to f1's local environment gone, the environment is also garbage-collected.

Super fundamental, yet a highly effective idea!

Functions returning other functions are perhaps one of the most common applications of closures in JavaScript programs. It's highly likely that once you start building complex programs, you'd use them too.

Practical applications

Let's finally see some practical-level, real world applications of function closures in JavaScript.

Module design pattern

As you start building large complex programs with a heck of logic involved, you'll soon start losing track of the program while developing it.

You have moments like 'what do I write next?', 'Oh that function needs this parameter as well!', 'this function is doing a lot of things', 'I am repeating quite a lot of code'. Things will soon become less manageable or completely unmanageable.

This would happen for sure. It happens with every developer as he/she starts on the trek of writing a large program. Thiis is where design patterns can help, though not the entirety.

A design pattern gives us an approach to solve a problem. It lays out rules and a general structure to follow when writing a program. There is a lot to cover on design patterns in programming, so much that a whole course could be devoted to them.

For now, we are just concerned with the module design pattern, and that within the bounds of JavaScript.

In the module design pattern, we group code into what's called a module and export back an object with public properties and methods that have access to private stuff defined inside the module.

The benefit of this design pattern is that we can consolidate code into one single location and in this way better organise our code bases, while at the same time be able to organise and maintain the code within each module much more effectively.

Here's how the pattern looks:

var moduleName = (function() {
   var privateVar;
   // more such private variables

   function privateFunction() {}
   // more such private functions

   return {
      publicProp: value,
      // more such public properties

      publicMethod: function() {},
      // more such public methods
   }
})();

For instance, let's suppose that we want to create an object with numerous methods to handle matrix operations such as addition, subtraction, multiplication, rotation, adding diagonals and so on and so forth.

Creating this library is possible using object literals, but not without a few problems.

Let's just create a quick dummy program to visualise everything much better:

// some variable defined here that are accessible
// by the entire code below
/* ... */

var matrix = {
   // create r x c matrix
   create: function(r, c = r) { /* ... */ },

   // return a + b
   sum: function(a, b) { /* ... */ },

   // return a - b
   diff: function(a, b) { /* ... */ },

   // return a x b
   product: function(a, b) { /* ... */ },

   // return the sum of the elements in the diagonal,
   // starting from the top-left corner of the a
   sumDiagonal: function(a) { /* ... */ },

   // more methods follow
   /* ... */
}

With the above library executed we could use things like matrix.sum() to add two matrices together, matrix.product() to multiply two matrices together and so on. Now, let's analyse the problems in this approach.

First of all, if some variables are required that are accessible throughout the module matrix, they would either be defined on the global scope, or as part of the object matrix.

In the former, we would unnecessarily be crowding the global scope with identifiers, meanwhile carrying the risk of name collisions with other scripts being used on the webpage.

On the otherhand, in the latter, we would have to namespace the identifier each time we need to access it because it is now a property of matrix.

This namespacing problem comes into existence when working inside the methods as well. For instance, let's suppose that a method of matrix has to use another method defined on it. With the literal approach above, we'd have to namespace the method each time we use it.

All these problems combined together can very soon become frustrating.

A better option is to use the module design pattern.

Let's quickly rewrite the code above using the module pattern:

var matrix = (function() {
   // variables that ought to be accessible by the
   // entire code below are defined here
   /* ... */

   // create r x c matrix
   function create(r, c = r) { /* ... */ }

   // return a + b
   function sum(a, b) { /* ... */ }

   // return a - b
   function diff(a, b) { /* ... */ }

   // return a x b
   function product(a, b) { /* ... */ }

   // return the sum of the elements in the diagonal,
   // starting from the top-left corner of the a
   function sumDiagonal(a) { /* ... */ }


   return {
      create: create,
      sum: sum,
      diff: diff,
      product: product,
      sumDiagonal: sumDiagonal,

      // more methods follow
      /* ... */
   }
})();

As you can see here, all the methods of the returned object are defined as local functions of the IIFE. This means that they could refer to one another without having to use the namespace matrix, thanks to closures.

This is a big achievement. The second one is that the global scope is neat and clean with no unnecessary names crowded.

As a whole, the module design pattern is a win-win situation in our case.

And once again, it's function closures that enable this design pattern to work without any issues in JavaScript. It won't be wrong to say that the module pattern is one of the most common applications of closures in JavaScript.

Higher-order functions

A function that takes in a function as argument and/or returns back a function is called a higher-order function.

For now, we are concerned with the latter part of this definition i.e. a function returning a function.

There will come many instances in a JavaScript program where you'll need a higher-order function. Let's see one such example.

Say we want to create a function that allows us to get an nth item from a given arithmetic sequence. For instance, if we were modeling the arithmetic sequence of all positive even integers, beginning at 2, the function could look something as follows:

function evens(n) {
   return 2 + n * 2;
}

console.log(evens(0), evens(1));
2 4

We provide in a value for n, beginning at 0, to which the function returns back the nth element in the sequence. In our case, since the sequence begins at 2, evens(0) returns 2, evens(1) returns 4 and so on.

If only the sequence of all even integers beginning at 2 was to be modeled, our evens() function would have been sufficient. However, if we were to allow to configure the arithmetic sequence as well, we'd have to change the function slightly.

The function would now look something as follows:

function elementFromSequence(n, a, d) {
   return a + n * d;
}

Let's model the sequence of all odd positive integers starting at 1 using this function, and get the value of the first, second and hundredth element from this sequence:

function elementFromSequence(n, a, d) {
   return a + n * d;
}

console.log(elementFromSequence(0, 1, 2), elementFromSequence(1, 1, 2), elementFromSequence(99, 1, 2));
1 3 199

As you can clearly see, there is one frustrating thing in this approach. If we are modeling the same sequence in a bunch of invocations, we ought to mention arguments for a and d again and again.

See how we have to repeatedly pass in the last two arguments 1 and 2 each time when calling elementFromSequence().

To solve this issue, we could use a very simple concept — function closures.

The trick is to create a function that takes in values for a and d once and then returns back a function which just takes a single argument n. This returned function remembers its lexical environment's variables a and d, and so could be used to inspect any nth element of the given sequence.

Shown below is the code:

function defineSequence(a, d) {
   return function(n) {
      return a + n * d;
   }
}

var evens = defineSequence(2, 2);
console.log(evens(0), evens(1));

var odds = defineSequence(1, 2);
console.log(odds(0), odds(1), odds(99));
2 4
1 3 199

The biggest benefit of this approach is that we can create an inspection function for any arithmetic sequence when calling defineSequence(), and then just pass in one single argument to the (returned) inspection function.

This gives us a lot of flexibility over the old, monotonous, and straightforward approach of passing in arguments for a and d right at the time when inspecting the nth element of a given sequence.