Last modified: Jul 17, 2026

JavaScript Closures and Variables Guide

JavaScript closures can feel tricky at first. But they are powerful tools. Closures let functions remember variables. This guide explains closures and variables simply. You will understand how they work together.

We will use short examples. Each example shows code and output. This makes learning easy. Let's start with the basics.

What is a Closure?

A closure is a function that remembers its outer variables. It can access them even after the outer function finishes. This is possible because JavaScript uses lexical scoping.

Lexical scoping means the inner function can access variables from its parent function. The closure "closes over" those variables. It keeps them alive.

Think of a closure as a backpack. The inner function carries a backpack of variables. It can use them anytime.

How Variables Work in Closures

Variables in closures work like normal variables. But they stay in memory. This is because the inner function still references them.

Consider this example. We have an outer function. It creates a variable. Then it returns an inner function.

 
function outer() {
  let message = "Hello from closure";
  
  function inner() {
    console.log(message);
  }
  
  return inner;
}

const myFunction = outer();
myFunction(); // Output: Hello from closure

Hello from closure

Here, inner() is a closure. It remembers the message variable. Even after outer() finishes, myFunction can still use message.

Variable Scope and Closures

Understanding variable scope is key. Closures only remember variables in their parent's scope. Variables declared with var, let, or const all behave differently.

Important:let and const are block-scoped. var is function-scoped. Closures work with all three, but the scope matters.

Let's see an example with var. It can cause issues in loops.

 
// Problem with var in loops
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 3, 3, 3 (after 1 second)

3
3
3

Why? Because var has function scope. The closure remembers the same i variable. By the time the timeout runs, i is 3.

Fix this by using let. It creates a new i for each iteration.

 
// Fix with let
for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 0, 1, 2 (after 1 second)

0
1
2

Now each closure gets its own i. This is a common pattern. For more on scope, read our JavaScript Variable Scope Explained guide.

Practical Example: Counter with Closure

Closures are great for creating private variables. You can make a counter that only a specific function can change.

 
function createCounter() {
  let count = 0; // Private variable
  
  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // Output: 1
console.log(counter.increment()); // Output: 2
console.log(counter.getCount());  // Output: 2
console.log(counter.decrement()); // Output: 1

1
2
2
1

Here, count is private. Only the methods inside createCounter can access it. This is a closure pattern. It protects data from outside changes.

Variable Shadowing in Closures

Sometimes an inner variable has the same name as an outer variable. This is called shadowing. The inner variable "shadows" the outer one.

 
let name = "Global";

function outer() {
  let name = "Outer";
  
  function inner() {
    let name = "Inner";
    console.log(name);
  }
  
  inner();
}

outer(); // Output: Inner
console.log(name); // Output: Global

Inner
Global

Each name is separate. The closure only sees its own scope first. This is important for debugging. Learn more in our JavaScript Variable Shadowing article.

Closures and Variable Types

Closures can remember any variable type. This includes strings, numbers, objects, and arrays. The closure holds a reference to the variable, not a copy.

If you change the variable later, the closure sees the change. This is because closures capture variables by reference.

 
function createLogger() {
  let data = { message: "Initial" };
  
  function log() {
    console.log(data.message);
  }
  
  data.message = "Updated";
  return log;
}

const logger = createLogger();
logger(); // Output: Updated

Updated

The closure sees the updated value. This is a key concept. For more on types, see our JavaScript Variable Types Guide.

Common Mistakes with Closures

Beginners often make mistakes. One common error is creating closures in loops with var. We already saw that.

Another mistake is forgetting that closures keep variables alive. This can cause memory leaks. If you have many closures, they may hold large objects.

Tip: Nullify variables when you don't need them. This helps garbage collection.

 
function bigDataProcessor() {
  let hugeArray = new Array(1000000).fill("data");
  
  return function() {
    console.log("Processing...");
    // Use hugeArray
  };
}

const process = bigDataProcessor();
// After use, set hugeArray to null in the closure? Not directly.
// Instead, nullify the reference in the outer scope.

You cannot nullify the variable inside the closure directly. But you can design your code to release references when done.

Closures in Event Handlers

Closures are common in event handlers. They remember the state when the handler was created.

 
function setupButton(buttonId, message) {
  const button = document.getElementById(buttonId);
  
  button.addEventListener('click', function() {
    alert(message);
  });
}

setupButton('myButton', 'Button clicked!');

Here, the click handler is a closure. It remembers message. Each button gets its own message.

Conclusion

JavaScript closures and variables work together. Closures let functions remember variables from their birth scope. This is powerful for private data, counters, and event handlers.

Remember these key points:

  • Closures capture variables by reference.
  • Use let in loops to avoid closure bugs.
  • Shadowing can cause confusion; use unique names.
  • Closures can hold any variable type.

Practice with small examples. Build counters and private modules. You will soon master closures. For more basics, check our JavaScript Variable Declaration Guide.

Keep coding and experimenting. Closures are a core part of JavaScript. They make your code cleaner and more powerful.