javascript

examples

examples.js
/**
 * =====================================================
 * 5.6 SCOPE AND CLOSURES - EXAMPLES
 * =====================================================
 * Understanding variable accessibility and closures
 */

// =====================================================
// 1. GLOBAL SCOPE
// =====================================================

console.log('--- Global Scope ---');

var globalVar = "I'm a global var";
let globalLet = "I'm a global let";
const globalConst = "I'm a global const";

function accessGlobals() {
  console.log(globalVar); // Accessible
  console.log(globalLet); // Accessible
  console.log(globalConst); // Accessible
}

accessGlobals();

// =====================================================
// 2. FUNCTION SCOPE
// =====================================================

console.log('\n--- Function Scope ---');

function functionScopeDemo() {
  var functionVar = "I'm function-scoped";
  let functionLet = "I'm also inside function";
  const functionConst = 'Me too';

  console.log(functionVar); // ✅ Accessible inside
  console.log(functionLet); // ✅ Accessible inside
  console.log(functionConst); // ✅ Accessible inside
}

functionScopeDemo();
// console.log(functionVar);  // ❌ ReferenceError

// =====================================================
// 3. BLOCK SCOPE
// =====================================================

console.log('\n--- Block Scope ---');

if (true) {
  var blockVar = 'var is NOT block-scoped';
  let blockLet = 'let IS block-scoped';
  const blockConst = 'const IS block-scoped';

  console.log('Inside block:', blockVar, blockLet, blockConst);
}

console.log('Outside block:', blockVar); // ✅ var leaks out
// console.log(blockLet);   // ❌ ReferenceError
// console.log(blockConst); // ❌ ReferenceError

// =====================================================
// 4. BLOCK SCOPE IN LOOPS
// =====================================================

console.log('\n--- Loop Scope ---');

// var - shared across all iterations
console.log('Using var:');
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('  var i:', i), 10);
}
// Output: 3, 3, 3 (all see final value)

// let - unique per iteration
console.log('Using let:');
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('  let j:', j), 20);
}
// Output: 0, 1, 2 (each has own copy)

// =====================================================
// 5. HOISTING
// =====================================================

console.log('\n--- Hoisting ---');

// var is hoisted with undefined
console.log('hoistedVar:', typeof hoistedVar); // undefined
var hoistedVar = "I'm hoisted";
console.log('hoistedVar:', hoistedVar);

// let/const are in Temporal Dead Zone (TDZ)
// console.log(hoistedLet);  // ReferenceError
let hoistedLet = 'I have TDZ';

// =====================================================
// 6. LEXICAL SCOPE
// =====================================================

console.log('\n--- Lexical Scope ---');

const outerVar = 'outer';

function outer() {
  const outerFunctionVar = 'outer function';

  function inner() {
    const innerVar = 'inner';

    // Can access all outer scopes
    console.log(innerVar); // inner
    console.log(outerFunctionVar); // outer function
    console.log(outerVar); // outer
  }

  inner();
}

outer();

// =====================================================
// 7. SCOPE CHAIN
// =====================================================

console.log('\n--- Scope Chain ---');

const level0 = 'Global level';

function level1() {
  const level1Var = 'Level 1';

  function level2() {
    const level2Var = 'Level 2';

    function level3() {
      const level3Var = 'Level 3';

      // Can access all levels above
      console.log(level3Var); // Level 3
      console.log(level2Var); // Level 2
      console.log(level1Var); // Level 1
      console.log(level0); // Global level
    }

    level3();
  }

  level2();
}

level1();

// =====================================================
// 8. BASIC CLOSURE
// =====================================================

console.log('\n--- Basic Closure ---');

function createCounter() {
  let count = 0; // Private variable via closure

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

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

// Each call creates independent closure
const counter2 = createCounter();
console.log(counter2()); // 1 (separate count)

// =====================================================
// 9. CLOSURE WITH MULTIPLE FUNCTIONS
// =====================================================

console.log('\n--- Closure with Multiple Functions ---');

function createCounterFull() {
  let count = 0;

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

const myCounter = createCounterFull();
console.log('Increment:', myCounter.increment()); // 1
console.log('Increment:', myCounter.increment()); // 2
console.log('Decrement:', myCounter.decrement()); // 1
console.log('Get count:', myCounter.getCount()); // 1
console.log('Reset:', myCounter.reset()); // 0

// =====================================================
// 10. DATA PRIVACY (ENCAPSULATION)
// =====================================================

console.log('\n--- Data Privacy ---');

function createBankAccount(initialBalance) {
  let balance = initialBalance; // Private!
  const transactions = []; // Private!

  return {
    deposit(amount) {
      if (amount > 0) {
        balance += amount;
        transactions.push({ type: 'deposit', amount });
        return balance;
      }
      return 'Invalid amount';
    },
    withdraw(amount) {
      if (amount > 0 && amount <= balance) {
        balance -= amount;
        transactions.push({ type: 'withdraw', amount });
        return balance;
      }
      return 'Insufficient funds';
    },
    getBalance() {
      return balance;
    },
    getTransactions() {
      return [...transactions]; // Return copy
    },
  };
}

const account = createBankAccount(100);
console.log('Initial:', account.getBalance()); // 100
account.deposit(50);
account.withdraw(30);
console.log('Final:', account.getBalance()); // 120
console.log('Transactions:', account.getTransactions());

// Cannot access directly
console.log('Direct access:', account.balance); // undefined

// =====================================================
// 11. FUNCTION FACTORY
// =====================================================

console.log('\n--- Function Factory ---');

function createMultiplier(multiplier) {
  return function (number) {
    return number * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);

console.log('double(10):', double(10)); // 20
console.log('triple(10):', triple(10)); // 30
console.log('quadruple(10):', quadruple(10)); // 40

// Greeting factory
function createGreeter(greeting) {
  return function (name) {
    return `${greeting}, ${name}!`;
  };
}

const sayHello = createGreeter('Hello');
const sayHi = createGreeter('Hi');

console.log(sayHello('Alice')); // "Hello, Alice!"
console.log(sayHi('Bob')); // "Hi, Bob!"

// =====================================================
// 12. MEMOIZATION
// =====================================================

console.log('\n--- Memoization ---');

function memoize(fn) {
  const cache = {};

  return function (...args) {
    const key = JSON.stringify(args);

    if (cache[key] !== undefined) {
      console.log('  Cache hit for', key);
      return cache[key];
    }

    console.log('  Computing for', key);
    const result = fn(...args);
    cache[key] = result;
    return result;
  };
}

// Example: Fibonacci with memoization
const fibonacci = memoize(function (n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log('fib(10):', fibonacci(10)); // Many computations
console.log('fib(10):', fibonacci(10)); // Cache hit!
console.log('fib(8):', fibonacci(8)); // Cache hit (computed during fib(10))

// =====================================================
// 13. MODULE PATTERN
// =====================================================

console.log('\n--- Module Pattern ---');

const Calculator = (function () {
  // Private variables
  let result = 0;
  let history = [];

  // Private function
  function addToHistory(operation) {
    history.push({ operation, result });
  }

  // Public API
  return {
    add(n) {
      result += n;
      addToHistory(`+ ${n}`);
      return this;
    },
    subtract(n) {
      result -= n;
      addToHistory(`- ${n}`);
      return this;
    },
    multiply(n) {
      result *= n;
      addToHistory(`* ${n}`);
      return this;
    },
    getResult() {
      return result;
    },
    getHistory() {
      return [...history];
    },
    reset() {
      result = 0;
      history = [];
      return this;
    },
  };
})();

Calculator.add(10).multiply(2).subtract(5);
console.log('Result:', Calculator.getResult()); // 15
console.log('History:', Calculator.getHistory());

// =====================================================
// 14. ONCE FUNCTION
// =====================================================

console.log('\n--- Once Function ---');

function once(fn) {
  let called = false;
  let result;

  return function (...args) {
    if (!called) {
      called = true;
      result = fn(...args);
      console.log('  Function executed');
    } else {
      console.log('  Function already called');
    }
    return result;
  };
}

const initialize = once((config) => {
  return `Initialized with: ${JSON.stringify(config)}`;
});

console.log(initialize({ debug: true }));
console.log(initialize({ debug: false })); // Ignored, returns first result

// =====================================================
// 15. LOOP CLOSURE PROBLEM AND SOLUTIONS
// =====================================================

console.log('\n--- Loop Closure Problem ---');

// Problem with var
console.log('Problem with var:');
const functions1 = [];
for (var x = 0; x < 3; x++) {
  functions1.push(function () {
    return x;
  });
}
console.log(functions1.map((fn) => fn())); // [3, 3, 3]

// Solution 1: let
console.log('Solution with let:');
const functions2 = [];
for (let x = 0; x < 3; x++) {
  functions2.push(function () {
    return x;
  });
}
console.log(functions2.map((fn) => fn())); // [0, 1, 2]

// Solution 2: IIFE
console.log('Solution with IIFE:');
const functions3 = [];
for (var x = 0; x < 3; x++) {
  (function (x) {
    functions3.push(function () {
      return x;
    });
  })(x);
}
console.log(functions3.map((fn) => fn())); // [0, 1, 2]

// =====================================================
// SUMMARY
// =====================================================

console.log('\n--- Summary ---');
console.log(`
SCOPE:
  • Global - Top level, everywhere
  • Function - Inside function only
  • Block - Inside {} with let/const

DECLARATIONS:
  • var - Function scope, hoisted
  • let - Block scope, TDZ
  • const - Block scope, TDZ, immutable

CLOSURES:
  • Function + lexical environment
  • Remembers outer variables
  • Enables data privacy
  • Use for factories, memoization, modules
`);
Examples - JavaScript Tutorial | DeepML