Lexical Scope and Closures

Understand how lexical scope and closures work in JavaScript, with simple explanations and clear examples.

Loading...
Lexical Scope and Closures

Lexical Scope and Closures are important concepts in JavaScript that help you understand how variables are accessed, stored, and remembered, especially in nested functions.

Lexical scope refers to determining the scope of variables and functions at compile time, based on where they are declared within the code.

When you define a function inside another function, the inner function can see and use the variables of the outer function but the outer function can’t see and use the variables of the inner function.

A Closure is created when an inner function has access to its outer function’s (parent scope) variables, even after the outer function (parent function) has finished executing.

Let's understand both with this example:

function outerFunc() {
  let outerVar = "I am a variable inside outer function!";
 
  function innerFunc() {
    console.log(outerVar);
  }
 
  return innerFunc;
}
 
const closureFunc = outerFunc();
closureFunc(); // Output: I am a variable inside outer function!

Here,

  • You have a function outerFunc and inside this function, you have a variable outerVar.
  • Inside the outerFunc function, there’s another function innerFunc and this innerFunc can access the outerVar variable because it’s defined in the outer function. This connection is possible due to lexical scope, which allows inner functions to reach out and use variables from their outer functions.
  • When you call outerFunc then it will return the innerFunc.
  • Then, you are storing the result of outerFunc() to closureFunc variable by const closureFunc = outerFunc();.
  • Now, closureFunc is essentially innerFunc because when you call outerFunc then it returns the innerFunc.
  • But closureFunc remembers the environment in which it was created and has access to outerVar.
  • Finally, on closureFunc(), you’re outside the original outerFunc but it still knows about outerVar. So, the output is: I am a variable inside outer function!.

Think of lexical scope like a set of rules that determine where in your code variables and functions live. If a function is inside another function, the inner function can use the outer function’s stuff, but the outer function can’t use the inner function’s stuff.

A closure is like a memory superpower for functions. Even after a function is done doing its thing, if it had an inner function, that inner function can still remember and use the things from its parent function.

A closure exists because of lexical scope. Lexical scope defines what a function has access to, and closures take advantage of that to remember those variables even after the outer function has finished.


Practical Use Case

Here's a practical example showing how closures can maintain state:

function createCounter() {
  let count = 0;
  return function () {
    count++;
    return count;
  };
}
 
const counter1 = createCounter();
const counter2 = createCounter();
 
console.log(counter1()); // Output: 1
console.log(counter1()); // Output: 2
console.log(counter2()); // Output: 1 (separate counter)
console.log(counter1()); // Output: 3

Here,

  • createCounter is a function that makes and returns another function.
  • Inside it, there's a private variable count set to 0.
  • The returned function increases count and gives back the new value.
  • This inner function remembers count, even though createCounter() is done running, that's a closure in action.
  • counter1 gets its own count starting from 0.
  • counter2 also gets a fresh count, separate from counter1.
  • counter1() runs and increases its own count: 1 → 2 → 3
  • counter2() is a different counter. It starts fresh at 1.
  • So both counter1 and counter2 have their own separate state, thanks to closures.
  • Every time you call createCounter(), it creates a new closure with its own count.
  • The inner function remembers the variable from the outer function, this is how closures work.
  • Closures help you keep variables private and independent from others.

Why Closures Are Useful

  • Private Variables: Closures let you create variables that can’t be seen or changed from outside the function.
  • Custom Functions: You can build functions that remember certain values and behave in a specific way.
  • Event Handling: Closures help your code remember things when working with events or delayed actions.
  • Data Hiding in Modules: They help keep some data hidden and organized when building parts of your app.

Things to Keep in Mind

While closures are powerful, there are some things to keep in mind:

  • Memory Use: Closures hold on to variables they use, which can stop those variables from being deleted. If not handled well, this can use more memory than needed.
  • Performance: Creating many closures can impact performance, especially in loops or frequently called functions.

Support my work!