🌱BeginnerFunctions

Scope and Closures

How JavaScript determines where a variable is accessible and the power of closures.

Overview

Scope defines where in your code a variable is accessible. JavaScript has three levels of scope: global (declared outside any function or block), function (variables declared inside a function), and block (variables declared with let/const inside {}). A closure is when a function "remembers" the variables from its outer scope even after that outer function has finished executing. Every function in JavaScript forms a closure over the variables it can see when it is defined. Closures are not a special opt-in feature they happen automatically. Closures power many common patterns: data privacy (private counters, module-like patterns), callbacks that need to access outer data, and factory functions that create customized functions. Understanding closures is essential to understanding how JavaScript really works.

Key Points

  • •Global scope: accessible everywhere; avoid polluting it with unnecessary global variables
  • •Function scope: var declarations are scoped to the containing function
  • •Block scope: let/const are scoped to the nearest surrounding {}
  • •A closure is a function that retains access to its outer scope variables after the outer function returns
  • •The inner function "closes over" the variables of the outer function hence the name closure
  • •Closures are how you create private state in JavaScript without classes

Examples

Block scope with let inner variable is invisible outside

function outer() {
  let x = 10;

  function inner() {
    let y = 20;
    console.log(x + y); // 30 inner sees outer's x
  }

  inner();
  // console.log(y); // ReferenceError: y not defined
}
outer();

A closure the counter example

function makeCounter() {
  let count = 0;          // private to makeCounter

  return function() {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

const counter2 = makeCounter(); // independent counter
console.log(counter2()); // 1

count is not accessible from outside makeCounter it's truly private. Each call to makeCounter creates a new independent closure.

Closure for configuration factory function

function makeMultiplier(factor) {
  return (number) => number * factor;
}

const double = makeMultiplier(2);
const triple = makeMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

Common Mistakes

  • Classic loop closure bug with var: all callbacks close over the same variable. Solution: use let or a closure immediately invoked.
  • Forgetting that each call to a factory function creates a separate closure with its own state
  • Confusing scope (where is the variable accessible?) with closure (does this function retain access to outer variables?)

Note

A quick mental model for closures: imagine each function carries a "backpack" of all the variables it could see when it was defined. Even when the outer function is gone, the inner function's backpack still has those values.

Exercises

Write JavaScript code to solve each exercise. Run it and check your output.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is a closure?