🏠 Home / Hub

🟨 JS Lesson 12 — Closures & Scope

← Back to JS Menu  |  🏠 Hub

1. Scope အမျိုးအစားများ

// Global scope — anywhere accessible
let globalVar = "I'm global";

function outer() {
  // Function (local) scope
  let outerVar = "I'm in outer";

  function inner() {
    // Nested function scope
    let innerVar = "I'm in inner";

    console.log(globalVar);  // ✅ visible
    console.log(outerVar);   // ✅ visible (lexical scope)
    console.log(innerVar);   // ✅ visible
  }

  console.log(globalVar);  // ✅
  console.log(outerVar);   // ✅
  // console.log(innerVar); // ❌ ReferenceError
}

// Block scope — let/const
{
  let blockVar = "block";
  const BLOCK_CONST = 42;
  var notBlock = "var leaks!";  // ⚠️ var ignores block scope!
}
// console.log(blockVar);  // ❌ ReferenceError
// console.log(notBlock);  // ✅ "var leaks!" — DON'T use var

// Lexical scope = function သည် ရေးထားသည့်နေရာ ရဲ့ scope ကို သိ

2. Closure ဆိုတာ ဘာလဲ?

Closure = function တစ်ခုသည် ၎င်းကို ဖန်တီးသည့် outer function ရဲ့ variables ကို outer function ပြီးဆုံးပြီးနောက်ဆိုလည်း မှတ်မိနေပြီး access ရနိုင်ခြင်း
// Basic closure
function makeCounter() {
  let count = 0;  // ← outer variable

  // Inner function — closes over "count"
  return function() {
    count++;
    return count;
  };
}

const counter1 = makeCounter();
const counter2 = makeCounter();  // separate closure!

counter1();  // 1
counter1();  // 2
counter1();  // 3

counter2();  // 1  ← own "count" — not shared!
counter2();  // 2

// count is PRIVATE — can't access from outside
// counter1.count → undefined

3. Closure Practical Uses

// 1. Private data (encapsulation without class)
function createWallet(initialBalance) {
  let balance = initialBalance;  // private!

  return {
    deposit(amount)  { balance += amount; return balance; },
    withdraw(amount) {
      if (amount > balance) return "Insufficient";
      balance -= amount;
      return balance;
    },
    getBalance() { return balance; }
  };
}

const wallet = createWallet(100);
wallet.deposit(50);    // 150
wallet.withdraw(30);   // 120
wallet.getBalance();   // 120
// wallet.balance → undefined (private!)

// 2. Function factory
function multiply(multiplier) {
  return (num) => num * multiplier;   // closes over multiplier
}

const double  = multiply(2);
const triple  = multiply(3);
const tenTimes = multiply(10);

double(5);    // 10
triple(5);    // 15
tenTimes(7);  // 70

// 3. Memoization (cache results)
function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key] !== undefined) {
      return cache[key];  // cached!
    }
    cache[key] = fn(...args);
    return cache[key];
  };
}

function slowSquare(n) {
  // imagine expensive calculation
  return n * n;
}

const fastSquare = memoize(slowSquare);
fastSquare(10);  // calculates: 100
fastSquare(10);  // from cache: 100 (instant!)
fastSquare(20);  // calculates: 400

4. IIFE — Immediately Invoked Function Expression

// IIFE = define and call immediately
// Use: isolate scope, avoid polluting global

(function() {
  const privateVar = "only here";
  console.log("IIFE runs immediately!");
  // privateVar — accessible here but not outside
})();

// Arrow function IIFE
(() => {
  console.log("Arrow IIFE");
})();

// IIFE with return value
const result = (function() {
  const x = 10;
  const y = 20;
  return x + y;
})();  // result = 30

// IIFE with parameters
(function(name) {
  console.log(`Hello, ${name}!`);
})("Ko Ko");

// Module pattern (pre-ES6 modules)
const Counter = (function() {
  let count = 0;   // private

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount()  { return count; }
  };
})();

Counter.increment();
Counter.increment();
Counter.getCount();  // 2
💡 Modern code uses ES Modules (import/export) instead of IIFE — but IIFE is still useful for quick isolated logic

5. Partial Application & Currying

// Partial application = pre-fill some arguments
function add(a, b, c) {
  return a + b + c;
}

function partial(fn, ...presetArgs) {
  return function(...laterArgs) {
    return fn(...presetArgs, ...laterArgs);
  };
}

const add5 = partial(add, 5);      // a=5 preset
const add5and10 = partial(add, 5, 10); // a=5, b=10 preset

add5(3, 2);      // 5+3+2 = 10
add5and10(7);    // 5+10+7 = 22

// Currying = transform f(a,b,c) → f(a)(b)(c)
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    }
    return function(...more) {
      return curried(...args, ...more);
    };
  };
}

const curriedAdd = curry((a, b, c) => a + b + c);
curriedAdd(1)(2)(3);    // 6
curriedAdd(1, 2)(3);    // 6
curriedAdd(1)(2, 3);    // 6
curriedAdd(1, 2, 3);    // 6

6. Live Demo — Counter with Closure

0 clicks

← JS 11  |  Next: JS 13 → Design Patterns →

📌 Study Checklist