JavaScript Closure: What is it & How do you use it?

Download Now: Introduction to JavaScript Guide
Athena Ozanich
Athena Ozanich

Updated:

Published:

Closure in JavaScript has a very different meaning than you might initially think, and it's way more fun too.

A young woman studying how closures work and ways she can use them to enhance the code she creates for her software.

In this post, you will discover what JavaScript Closure is and how to use it in your programming code. You will also learn more than one way to perform closure in JavaScript. Finally, you will see code examples of how to complete your closure to support everything you have learned.

Let's get started.

Download Now: An Introduction to JavaScript  [Free Guide]

What is closure in JavaScript?

Closure in JavaScript is a form of lexical scoping used to preserve variables from the outer scope of a function in the inner scope of a function. Lexical scoping is the process used to define the scope of a variable by its position in the source code. Check out the following video to learn more about JavaScript closures.

When you define a function, you can only access its variables from within the function. Attempting to access variables from outside the function will result in a scope error; this is where closure comes in handy.

Let's look at scope with a code example showcasing variables declared in the global and local scopes.

let message = "Hello"; function buildGreeting() { let audience = "World"; console.log(message + " " + audience); }

In the above example, there are two scopes; the first is the global scope, where the variable "message" is declared. The second is the function's local scope, where the variable "audience" is declared. In this code, the function can access the variable "message"; however, "audience" is only accessible to the function. If you try to access the function's local variable, the code will throw an error in response.

In the. example below, we'll try and call the "audience" variable from the global scope, which should result in an errror telling us that this variable is undefined.

let message = 'Hello'; function buildGreeting() { let audience = 'World'; let message = 'Hello'; // Duplicate variable declaration function buildGreeting() { let audience = 'World'; console.log(message + ' ' + audience); } } console.log(audience); // 'audience' is not defined in this scope

This behavior is important because it can help you understand how to avoid errors in your code, and it helps explain how Lexical scoping works. It's interesting to note that Lexical scoping allows a nested scope that can access a variable within its outer function. Let's see what that looks like in the code example below.

Any function declared at this level is technically a nested scope. Each function declared at the global level has its own scope and is not accessible from the global scope. Furthermore, the scope of a function is inaccessible from other functions. Let's look at that in the following code block.

function buildGreeting() { let message = "Hello"; } function greetUser() { let audience = "World"; console.log(message); } greetUser();

Attempting to run the code above would result in an error message when the greetUser() function gets called.

Once the error occurs, the code will stop running to help keep debugging simple and make it easier to identify the cause of the error message.

More on JavaScript Scope

In the following example, there are two instances of nested scopes; the first is the global scope, named so because it's the highest-level scope.

let message = "Hello"; function buildGreeting() { console.log(message); let audience = "World"; function greetUser() { console.log(message + " " + audience); } greetUser(); } buildGreeting();

The global scope is where the message variable is declared. The greetUser() function exists within the buildGreeting() function; this means it's local to its parent function.

JavaScript Closure

As a result of this local scoping, it also means that the greetUser() function is not available from the global scope. However, it's interesting to note that we can access the greetUser() function through the buildGreeting() function.

To make the greetUser() function accessible from the global scope, you return the greetUser() function from within the buildGreeting() function. Then you assign the buildGreeting() function to a variable and call that variable like a function.

function buildGreeting() { let message = "Hello"; function greetUser() { console.log(message); } return greetUser; } let hello = buildGreeting(); hello();

There are more ways to create closure in JavaScript, and in the following code block, you can see an example you may find yourself using.

function buildGreeting(message) { return function (audience) { return message + " " + audience; }; } let greeting1 = buildGreeting("Hi"); let greeting2 = buildGreeting("Hello"); console.log(greeting1("User")); // Hi User console.log(greeting2('World')); // Hello World

In this example, you create a function called buildGreeting() and return a function that returns a string composed of two variables. The variables get passed in at two points in this code. The first is passed in when assigning the function to a variable, as seen below.

let greeting1 = buildGreeting("Hi");

After the assignment, you call the variable a function passing in the following value as an argument for the inner function. In this case, the function call is performed in a console log, allowing you to see the string that the inner function creates.

console.log(greeting1("User")); // Hi User

JavaScript Closures and Loops

Creating closure in JavaScript becomes a little more complicated when working with loops, as it causes undesirable behavior. For example, consider the following function, which uses a setTimeout function within a loop.

for (var id = 0; id < 3; id++) { // The setTimeout function uses a callback, and by the time the callback is executed, // the loop has already completed, so 'id' will always be 3 in all setTimeout callbacks. setTimeout(function () { console.log('seconds: ' + id); }, id * 1000); }

In the above code, the loop runs three times, and the setTimeout function waits for a specified time to pass before running the code within it. You might expect the code to run three times, reflecting the id's index value at that loop's time like with the output below.

"seconds: 0" "seconds: 1" "seconds: 2" 

However, in this case, the loops run, and the id variable updates accordingly. Since the code runs from the setTimeout function, the id has already been updated to its maximum value. Because all three iterations of the loop share the same scope, the setTimeout function creates a closure shared by each loop.

This fact means that the message printed is not what you expect. Instead, the console log reflects the final value of the id.

"seconds: 3" "seconds: 3" "seconds: 3" 

ES6 let Keyword

You can alleviate this issue, you can use the JavaScript ES6 let keyword to ensure the code within the if block runs as expected. A new scope gets created for each loop iteration using the let keyword to declare the index value. Let's see how you can do this in the example below.

for (let id = 0; id < 3; id++) { // The use of 'let' creates a block-scoped variable, fixing the closure issue. // Now each setTimeout callback captures the correct value of 'id'. setTimeout(function () { console.log('seconds: ' + id); }, id * 1000); }

The code above results in the expected behavior instead of the setTimeout function running after the loop completes. The loop runs, and the setTimeout function gets assigned with the id to each iteration of the loop. You can see the resulting output below.

"seconds: 0" "seconds: 1" "seconds: 2" 

IIFE and Closures

Another way to avoid this issue with closures in a loop is to use the IIFE (Immediately Invoked Function Expression) syntax, which forces an immediate invocation of the setTimeout function as soon as the loop runs. So instead of essentially stacking the setTimeout function and waiting for the loop to finish, then execute the code, the setTimeout runs as soon as the loop starts, which is the expected behavior. Let's see what the syntax for IIFE looks like below.

for (var id = 1; id <= 3; id++) { // Using an immediately-invoked function expression (IIFE) to create a new scope for each iteration. // This helps avoid the closure issue with setTimeout. (function (id) { setTimeout(function () { console.log('seconds: ' + id); }, id * 1000); })(id); }

In the above code, the loops run, and the function is invoked immediately on each iteration. The setTimeout function then starts to execute immediately, preserving the state of the id in each iteration. However, it is worth noting that the ES6 approach is a much cleaner solution to this issue; however, there may be times when IIFE works better.

How to Move Forward With JavaScript Closures

When learning any programming concept, the best way to move forward is to practice what you've learned. Practice is even more critical with closures because the subject matter can be tricky. As a result of this fact, it is beneficial to explore and even create closures to learn what closures can look like in different situations. Each case is a little different, and you can use closures to perform many tasks that would otherwise be much more difficult. Identifying closures is the best way to solidify your understanding of closures and how to create them.

New Call-to-action

Topics: Javascript

Related Articles

We're committed to your privacy. HubSpot uses the information you provide to us to contact you about our relevant content, products, and services. You may unsubscribe from these communications at any time. For more information, check out our Privacy Policy.

Learn more about one of the world's most popular programming languages.

CMS Hub is flexible for marketers, powerful for developers, and gives customers a personalized, secure experience

START FREE OR GET A DEMO