Functions and scope

JavaScript Closures explained

Learn how closures work in JavaScript: a function runs with the lexical scope where it was defined, so it remembers and can access its parent scope variables.

If you have written a function in JavaScript, you have already used a closure.

A closure means a function runs with the scope that existed when you defined it, not with whatever scope exists when you call it later.

The function keeps access to variables from its parent scope. Think of it as carrying a small bag of names from where it was created.

Here is the simplest version:

const bark = dog => {
  const say = `${dog} barked!`
  ;(() => console.log(say))()
}

bark(`Roger`)

This logs Roger barked!.

What if you return the inner function instead of calling it right away?

const prepareBark = dog => {
  const say = `${dog} barked!`
  return () => console.log(say)
}

const bark = prepareBark(`Roger`)

bark()

Still logs Roger barked!. The returned function remembers say from prepareBark.

Now call prepareBark twice for two dogs:

const prepareBark = dog => {
  const say = `${dog} barked!`
  return () => {
    console.log(say)
  }
}

const rogerBark = prepareBark(`Roger`)
const sydBark = prepareBark(`Syd`)

rogerBark()
sydBark()

Output:

Roger barked!
Syd barked!

Each returned function keeps its own say. Calling prepareBark('Syd') does not change what rogerBark remembers.

That is the closure: the inner function holds on to its outer scope even after the outer function finished running.

Closures show up in real code for counters, private state, and factory functions that return configured helpers. The pattern is the same every time: outer variables survive because an inner function still references them.

Each call to prepareBark('Roger') creates a fresh scope. That is why rogerBark and sydBark do not share state.

Module patterns in older codebases often return an object of public methods from an IIFE while keeping private variables inside that same outer function. Same idea, different packaging.

Try changing prepareBark to log dog instead of say. You will see the same pattern with the parameter name.

Loop variables in for loops used to share one closure binding with var. Modern let in a for loop gives each iteration its own binding, which avoids a classic closure gotcha.

Lesson completed