JavaScript Closures Overview
Getting Started
As a versatile feature of JavaScript, closures enable us to create functions that have access to variables outside of their own scope, allowing us to build more modular and maintainable code.
Pure Functions
Before we dive into closures, let's take a quick look at pure functions. So, a pure function only depends on its arguments and internal data; it's fully self-contained and doesn't reference any variables outside of its scope.
function add(a, b) {
return a + b;
}
Our example function depends only on its arguments a
and b
and doesn't reference any variables outside of its own scope.
Closures
When we call a function that references variables from the surrounding environment, the JavaScript interpreter creates a closure to store those variables in a place in memory where they can be accessed later.
Simply put, closures are functions that reference variables outside of their own scope.
Creating a Closure
We can create a closure by defining an outer function that contains the state, and then an inner function that operates on it. The data contained in the outer function will not leak out to the surrounding environment, but the inner function has access to data defined in the outer function scope. Like so:
function outer() {
const data = 'Hello, World!';
return function inner() {
console.log(data);
};
}
const innerFn = outer();
innerFn(); // logs 'Hello, World!'
Let's dive into more detail and explain the syntax.
Here, the outer
function defines a variable data
, and then returns the inner function inner that logs the value of our data. Upon calling outer
, it returns inner, and we can then call inner to log the value of the data which in this case is the string 'Hello, World!'
.
Memory and Performance
Closures require more memory and processing power than pure functions since they need to store data in memory indefinitely. However, they provide many practical benefits, such as data encapsulation to prevent data leakage or exposure where it's not needed. Naturally, having proper judgment, and understanding these implications comes with a lot of exposure to the language.
Callbacks and Closures
We can use closures to create a "function factory" that takes an argument and returns a brand new function, which can then be passed along to other ones that expect a callback.
To demonstrate how closures encapsulate the state and create reusable functions, we'll use the following example:
function alertExample(message) {
return () => {
alert(`${message}`)
}
}
const alertAdvice = alertExample('Be patient.');
const alertWarning = alertExample('Be careful!');
alertAdvice(); // displays 'Be patient.'
alertWarning(); // displays 'Be careful!'
By creating two instances of alertExample
with different messages, we receive two unique functions that we can reuse throughout our application. These functions use the same inner function but contain contrasting parameters.
The Infamous Closure Interview Question
What we see below, is a common JavaScript trick question that might prove to be challenging if you don't understand the closure caveat in the example.
The Question
What would be the log output of this code?
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
The Answer
Feels unintuitive, but the output of the code is 3 3 3
.
To understand why this happens, we need to understand the difference between var
and let
in terms of scope. When we use var
in a for loop, that variable actually gets hoisted up into the parent scope, which in this case would be the global one.
So, when var
is used to declare the variable,i
is globally scoped. Also, when we reference i
inside the setTimeout
callback, it's referencing the same variable from the global scope, which has been incremented to 3 by the end of the loop.
In contrast, if we use let
, the variable i
will be scoped to the loop, meaning that each iteration will have its own variable i
.
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
With this approach, the output is 0 1 2
, which is what we would expect.
Additional Resources
An Overview of Callback Functions