JavaScript/TypeScript: Clarifying Closures

Brennan D Baraban
Jul 19 · 5 min read

Closures can be confusing in JavaScript. But, the truth is — you’ve most likely used them before! Let me explain.


TERMINOLOGY

In referencing behavior of the JavaScript engine, I reference any of the major implementations, whether it be Firefox’s SpiderMonkey or Chrome’s V8 on the browser-side, or Node.js on the server-side. The internal behavior of the three is identical for the purposes of this article.

All information discussed in this article is specific to the ES6 standard.

Finally, note that while I will be describing behavior of the JavaScript engine, code will be exemplified in TypeScript. TypeScript is a static-typed superset language that compiles to JavaScript. Again, the internal behavior of the two is identical, but TypeScript is more verbose for both type safety and exemplary purposes.


CLOSURES — PERSISTENT SCOPE

Alright, ready for this?

All code blocks in JavaScript are closures.

No, I’m not kidding! Closures are nothing more than a fancy term for describing an inherent feature of the JavaScript engine — scope. Or, more specifically, persistent scope.

Let me back up a step; this will make more sense in context (pun intended). Recall that JavaScript scripts are executed within a global execution environment. This is an overarching context within which a given script is run, and, more relevantly to our purposes, variables stored. You may prefer thinking about the global execution environment as a global scope.

For instance, say we have a script that declares a variable variable1 defined with the number 10. This variable is stored in the global scope like so:

Within the global execution context, any child processes (think, function calls, conditional blocks, or anything in curly braces {}) are instantiated their own lexical execution contexts. These nested code blocks feature their own local scopes within which they store locally-defined variables.

In the above, execution of the function func involves instantiation of a new lexical environment within which it stores variable2, inaccessible by the global context.

OK, here’s the kicker — lexical execution contexts always include a reference to their parent environment. This reference enables lexical contexts to access the scopes of their parent environments.

In fact, even the global execution context includes a parent reference — it merely points to null.

When a lexical execution context attempts to access a variable, it goes through a strict process. First, it looks for the variable within its local scope. If the variable is locally defined, it is used right then and there. Otherwise, the JavaScript engine uses the lexical context’s outer reference to check for the variable in the parent scope.

This process of searching outer environments will continue until either the variable is found or the engine hits the null referenced by the global environment; at that point it can determine that the variable does not exist.

Output:

2
1

Now, check it out — you just learned what a closure is! Yup, closures are nothing more than a term used to describe the concept that lexical execution contexts contain references to their parent environments.

In short, closures describe persistent scope, and the ability of inner code blocks to reference outer variables.

In minimal — any code block within curly brackets {} is a closure.

CLOSURES — WHEN AND WHERE

Like I said — you’ve most likely been using closures throughout your JavaScript/TypeScript programs, even if you didn’t necessarily know it until now. If you’ve ever written a code block that uses a variable declared beyond its local scope, you’ve used a closure.

The classic example, of course, is the counter function.

Output:

Before call to counter: 0
After call to counter: 1

In the above, we declare the variable count before defining the iterator function counter. Nonetheless, the function can still access and iterate count — it is a closure, after all!

Let’s think of a more fun example, however. Consider the following grocery list program that manages a running object of products to buy — the object stores items in the format <product name>: <count>.

Output:

Initial list:
-> bananas: 0
-> cookies: 0
-> cereal: 0
Final list:
-> bananas: 1
-> cookies: 4
-> cereal: 2

Closures make this program convenient. We can declare our grocery list object globally, and then reference it within item-adding functions no problem, without having to manually pass it around.

So, closures are pretty cool, and actually quite common. If you ever get stuck remembering what closures are, just keep in mind — you use them all the time!


READ MORE OF MY JAVASCRIPT/TYPESCRIPT POSTS


Brennan D Baraban

Written by

Full-stack software engineer