Lab 04: Closures & Scope

Time: 30 minutes | Level: Practitioner | Docker: docker run -it --rm node:20-alpine sh

Overview

Deep dive into JavaScript's lexical scope, closures, IIFE pattern, module pattern, factory functions, closure pitfalls, and WeakRef for memory-safe closures.


Step 1: Lexical Scope

const globalVar = 'global';

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

  function inner() {
    const innerVar = 'inner';
    // inner has access to outer and global scope
    console.log(globalVar); // 'global'
    console.log(outerVar);  // 'outer'
    console.log(innerVar);  // 'inner'
  }

  inner();
  // console.log(innerVar); // ReferenceError — not accessible here
}

outer();

// Block scope (let/const)
{
  let blockScoped = 'block';
  const alsoBlock = 'also block';
  console.log(blockScoped); // 'block'
}
// console.log(blockScoped); // ReferenceError

💡 JavaScript uses lexical (static) scope — a function's scope is determined by where it's defined, not where it's called.


Step 2: Closures


Step 3: IIFE (Immediately Invoked Function Expression)

💡 IIFE was the primary way to create private scope before ES modules. Still useful for async initialization.


Step 4: Module Pattern


Step 5: Factory Functions


Step 6: Closure Pitfalls & Fixes

💡 let and const are block-scoped, so each loop iteration creates a new binding.


Step 7: WeakRef for Memory-Safe Closures


Step 8: Capstone — Memoization with Closures

Run verification:

📸 Verified Output:


Summary

Concept
Description
Example

Lexical scope

Scope determined by code position

Nested functions access outer vars

Closure

Function + its lexical environment

Counter, memoize, factory

IIFE

Function called immediately

(() => {})()

Module pattern

IIFE returning public API

Private state + public methods

Factory function

Function returning objects

createCounter(), createAccount()

var pitfall

var leaks from blocks/loops

Use let/const instead

WeakRef

Reference without preventing GC

Caches that auto-expire

Last updated