Scope & Closures

Where variables live, and how functions remember their environment.

Scope Types

Where variables are visible and accessible.

🌍 Global Scope

const APP_NAME = "VisualJS"; // accessible everywhere

📦 Function Scope

function greet() {
  const message = "Hello"; // only inside greet()
}

🧱 Block Scope

if (true) {
  let x = 10; // only inside this {}
  const y = 20;
}

var

Function-scoped

let

Block-scoped

const

Block-scoped

Scope Chain

Inner scopes look outward until they find the variable.

const global = "G";

function outer() {
  const outerVar = "O";
  
  function inner() {
    const innerVar = "I";
    console.log(innerVar); // "I" — found locally
    console.log(outerVar); // "O" — found in outer
    console.log(global);   // "G" — found in global
  }
  inner();
}
// inner → outer → global (chain lookup)

💡 Scope lookup goes outward only. Outer scopes can never access inner variables. Siblings can't access each other.

Closures

A function that remembers its surrounding scope even after it executes.

function createCounter() {
  let count = 0;  // enclosed variable
  return function() {
    count++;
    return count;
  };
}

createCounter() defines a variable and returns a function that references it.

1 / 3

// Live closure — count persists between clicks:

0

count is private — only counter() can access it

Closure Patterns

Real-world uses you'll see everywhere.

1. Private State (Module Pattern)

function createWallet(initial) {
  let balance = initial;
  return {
    deposit: (amount) => balance += amount,
    withdraw: (amount) => balance -= amount,
    getBalance: () => balance,
  };
}
const wallet = createWallet(100);
wallet.deposit(50);  // balance = 150
wallet.getBalance(); // 150
// wallet.balance → undefined (private!)

2. Function Factory

function multiply(factor) {
  return (number) => number * factor;
}
const double = multiply(2);
const triple = multiply(3);
double(5);  // 10
triple(5);  // 15

3. Memoization / Caching

function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

Common Pitfalls

Watch out for these closure mistakes.

// The classic loop bug:
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3  ← all same!

var is function-scoped. All callbacks share the SAME 'i' variable, which is 3 by the time they run.

💡 var + async = shared reference trap
1 / 3

FAQ

Common questions about scope and closures.