Vanta Logo
SPONSOR
Automate SOC 2 & ISO 27001 compliance with Vanta. Get $1,000 off.
Published
5 min read
Up to date

Trevor I. Lasn

Staff Software Engineer, Engineering Manager

Understanding JavaScript Closures With Examples

Closures are essential for creating functions that maintain state, without relying on global variables.

I came across an interesting question on Reddit. Someone was having trouble understanding why two different approaches to counting values in JavaScript didn’t behave the same way.

The Reddit user expected that two similar ways of incrementing a counter would reset the count to zero, but they didn’t.

The Reddit user was confused. They thought the first and second approach should behave the same way, but they don’t. The first approach resets the count to zero every time, while the second approach continues from where it left off.

Why Does This Happen?

The key difference lies in closures. If that word makes your head spin, don’t worry—I’ll break it down.

A closure in JavaScript happens when a function can “remember” and access variables from the outer scope, even after that outer function has finished executing. This allows the inner function to maintain access to the variables in place when the function was created.

  • outerFunction() is called, which creates a local variable outerVariable and defines an innerFunction.
  • outerFunction() then returns innerFunction, but here’s the magic part: even though outerFunction has finished running, the innerFunction can still access outerVariable. This is because of closure.

The innerFunction “remembers” the environment where it was created, which includes the outerVariable variable. When you call closureFunction(), it can still access and log the outerVariable even though the outerFunction() has completed execution.

Simple Counter Using Closures

  • createCounter() defines a local count variable and returns a function that increments and logs the value of count.
  • Even after createCounter() finishes, the returned function still has access to the count variable thanks to closure. So, every time you call counter(), it updates the same count variable, preserving the value between calls.

Closure with Parameters

Let’s expand the counter to take an initial value as a parameter.

  • This time, we pass an initial value (startValue) to createCounter(). The count variable is initialized with this value.
  • When you create two counters (counterFrom10 and counterFrom5), each has its independent count variable, but both preserve their state between calls. Each closure remembers its starting value.

Multiple Functions Sharing the Same Closure

Now, let’s say you want to create a counter that can increment, decrement, or reset the count. Here’s how you can do that using closures.

  • We’re returning an object that contains multiple functions, all of which share access to the same count variable.
  • Each method (increment, decrement, reset) is a closure, and they all share the same count state. So, whether you increment, decrement, or reset, they’re all working with the same count variable that’s kept alive by the closure.

First Approach: Why Does It Reset?

In the first approach:

Every time you call createCount(), you are creating a new instance of the count variable.

  • The first createCount() call creates a fresh count = 0, increments it to 1, and logs it.
  • The second createCount() call does the same thing: it creates a new count = 0 and increments it again to 1.

This is why the count resets in this approach—it’s like hitting the “reset button” every time you call createCount().

Second Approach: Why Does It Continue?

Now, look at the second approach:

Here, you’re only calling createCount() once. This is crucial because you’re saving the result (which is the increment function) into the counter variable.

  • The first time you call counter.increment(), it uses the count that was initialized as 0 in the original createCount(). It increments the count to 1.
  • The second time you call counter.increment(), it doesn’t reset count. It just uses the same count value that’s now 1 and increments it to 2.

Since you’re not calling createCount() again, you’re using the same closure, and the count value persists between calls.

Closures are super useful because they let you “remember” values in your functions, even after those functions have finished running. In real-world applications, closures are the backbone of things like:

  • Event listeners that need to remember some information.
  • Callbacks where the function is called much later but still needs access to earlier variables.
  • Private variables, where you want to keep a value hidden from the outside world.

If you found this article helpful, you might enjoy my free newsletter. I share developer tips and insights to help you grow your skills and career.


More Articles You Might Enjoy

If you enjoyed this article, you might find these related pieces interesting as well. If you like what I have to say, please check out the sponsors who are supporting me. Much appreciated!

Javascript
5 min read

Why Browsers Block CSS File Modifications

Understanding CSS Object Model (CSSOM), browser security and practical alternatives for dynamic styling

Oct 25, 2024
Read article
Javascript
7 min read

JavaScript Truthy and Falsy: A Deep Dive

Grasp JavaScript's type coercion with practical examples and avoid common pitfalls

Oct 27, 2024
Read article
Javascript
4 min read

JavaScript Import Attributes (ES2025)

Understanding the new import attributes syntax and why we can't rely on file extensions alone

Nov 10, 2024
Read article
Javascript
3 min read

JavaScript's ??= Operator: Default Values Made Simple

A guide to using ??= in JavaScript to handle null and undefined values elegantly

Nov 5, 2024
Read article
Javascript
6 min read

AggregateError in JavaScript

AggregateError helps you handle multiple errors at once in JavaScript. This makes your code easier to manage and more reliable.

Sep 2, 2024
Read article
Javascript
6 min read

Micro Frontends: The LEGO Approach to Web Development

Explore the concept of micro frontends in web development, understand their benefits, and learn when this architectural approach is most effective for building scalable applications.

Oct 2, 2024
Read article
Javascript
7 min read

What's New in Express.js v5.0

A detailed look at the key changes and improvements in Express v5.0 and how to migrate your app

Sep 16, 2024
Read article
Javascript
7 min read

JavaScript Operators: '||' vs '&&' vs '??'

Master JavaScript logical operators with practical examples and best practices

Oct 26, 2024
Read article
Javascript
5 min read

Precise Decimal Math in JavaScript with Fraction.js

How to handle exact decimal calculations in JavaScript when floating-point precision isn't good enough

Nov 16, 2024
Read article

Become a better engineer

Here are engineering resources I've personally vetted and use. They focus on skills you'll actually need to build and scale real projects - the kind of experience that gets you hired or promoted.

Many companies have a fixed annual stipend per engineer (e.g. $2,000) for use towards learning resources. If your company offers this stipend, you can forward them your invoices directly for reimbursement. By using my affiliate links, you support my work and get a discount at the same!


This article was originally published on https://www.trevorlasn.com/blog/understanding-javascript-closures. It was written by a human and polished using grammar tools for clarity.