CodeMIRAI
0 XP

Scope and closures

2 min read 5 runnable examples Quiz included

Scope

Scope decides where a variable can be used.

  • Variables declared with let and const are block-scoped: they exist only inside the { } where they're declared.
  • Variables declared inside a function are visible only inside that function.
  • Variables declared outside everything are global.
JavaScript
const appName = "CodeMIRAI";        // global

function show() {
  const message = "inside show";    // function scope
  if (true) {
    const inner = "inside the if";  // block scope
    console.log(appName, message, inner);
  }
  console.log(typeof inner);        // "undefined": inner doesn't exist here
}
show();
console.log(typeof message);

Inner scopes can see outer variables; outer scopes can't see inner ones.

Closures

A closure is a function that remembers the variables from the place it was created, even after that place has finished running.

function makeCounter() {let count = 0;return () => ++count;}const next = makeCounter();next: the inner functionits backpack (closure)count = 2next(); next(); → 1, 2makeCounter has returned, but count lives on inside next
A function remembers the variables around it when it was created. That is a closure, and it's how private state works in JavaScript.
JavaScript
function makeCounter() {
  let count = 0;                 // private to this counter
  return function () {
    count++;
    return count;
  };
}

const counterA = makeCounter();
const counterB = makeCounter();
console.log(counterA(), counterA(), counterA());
console.log(counterB());

makeCounter has finished, yet the returned function still reaches count. Each call to makeCounter creates a separate count, so the two counters don't interfere.

Private state

Closures give you data that nothing outside can touch:

JavaScript
function createWallet(start) {
  let balance = start;
  return {
    deposit(amount) { balance += amount; },
    spend(amount) {
      if (amount > balance) return "Not enough money";
      balance -= amount;
      return "OK";
    },
    get balance() { return balance; },
  };
}

const w = createWallet(100);
w.deposit(50);
console.log(w.spend(500), w.spend(30), w.balance);
console.log(w.balanceValue);       // there's no way to reach the variable directly

Function factories

JavaScript
const multiplier = (factor) => (n) => n * factor;
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(10), triple(10));

The classic loop gotcha

With let, each loop iteration gets its own variable, so closures capture the right value:

JavaScript
const fns = [];
for (let i = 0; i < 3; i++) {
  fns.push(() => i);
}
console.log(fns.map(f => f()));    // [0, 1, 2]

With the old var, all three would share one variable and print [3, 3, 3], which is one more reason modern code uses let.

Finished this lesson?Mark it complete to earn +20 XP and move your progress on.