Docs

5.6-Scope-Closures

5.6 Scope and Closures

Overview

Understanding scope and closures is fundamental to mastering JavaScript. Scope determines where variables are accessible, while closures allow functions to remember and access variables from their parent scope even after the parent has finished executing.


Table of Contents

  1. •What is Scope?
  2. •Types of Scope
  3. •Variable Declarations and Scope
  4. •Lexical Scope
  5. •Scope Chain
  6. •What are Closures?
  7. •Practical Closure Examples
  8. •Closure Use Cases
  9. •Common Closure Pitfalls
  10. •Memory Considerations
  11. •Best Practices

What is Scope?

Scope is the context in which variables and expressions are accessible.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                        GLOBAL SCOPE                      │
│  Variables accessible everywhere                         │
│                                                          │
│   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
│   │               FUNCTION SCOPE                     │   │
│   │  Variables accessible within the function        │   │
│   │                                                  │   │
│   │   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │   │
│   │   │            BLOCK SCOPE                   │  │   │
│   │   │  Variables in {}, if, for, while         │  │   │
│   │   │  (only let and const)                    │  │   │
│   │   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │   │
│   │                                                  │   │
│   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Basic Example

const globalVar = "I'm global";

function exampleFunction() {
  const functionVar = "I'm function-scoped";

  if (true) {
    const blockVar = "I'm block-scoped";
    console.log(globalVar); // āœ… Accessible
    console.log(functionVar); // āœ… Accessible
    console.log(blockVar); // āœ… Accessible
  }

  console.log(blockVar); // āŒ ReferenceError
}

console.log(functionVar); // āŒ ReferenceError

Types of Scope

1. Global Scope

Variables declared outside any function or block.

// Global scope
var globalVar = 'global var';
let globalLet = 'global let';
const globalConst = 'global const';

// These are accessible everywhere
function accessGlobals() {
  console.log(globalVar); // āœ…
  console.log(globalLet); // āœ…
  console.log(globalConst); // āœ…
}

2. Function Scope

Variables declared inside a function are only accessible within that function.

function myFunction() {
  var functionVar = "I'm only in this function";
  let functionLet = 'Me too';
  const functionConst = 'Same here';

  console.log(functionVar); // āœ… Accessible
}

console.log(functionVar); // āŒ ReferenceError

3. Block Scope

let and const are block-scoped; var is not.

if (true) {
  var varVariable = 'var is NOT block-scoped';
  let letVariable = 'let IS block-scoped';
  const constVariable = 'const IS block-scoped';
}

console.log(varVariable); // āœ… "var is NOT block-scoped"
console.log(letVariable); // āŒ ReferenceError
console.log(constVariable); // āŒ ReferenceError

Block Scope in Loops

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

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

Variable Declarations and Scope

DeclarationScopeHoistingRe-declaration
varFunctionYes (undefined)Yes
letBlockNo (TDZ)No
constBlockNo (TDZ)No

Hoisting Behavior

// var hoisting
console.log(x); // undefined (hoisted)
var x = 5;

// let/const - Temporal Dead Zone (TDZ)
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;

Temporal Dead Zone (TDZ)

// TDZ starts at block entry
{
  // TDZ for 'value' starts here
  console.log(value); // ReferenceError
  let value = 'initialized'; // TDZ ends
}

Lexical Scope

Scope is determined by where functions are written (defined), not where they are called.

const name = 'Global';

function outer() {
  const name = 'Outer';

  function inner() {
    // 'inner' looks up scope chain to find 'name'
    console.log(name); // "Outer" (lexical parent)
  }

  return inner;
}

const innerFn = outer();
innerFn(); // "Outer" - uses scope where defined

Visual Representation

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Global Scope                                         │
│  └── outer() defined here                            │
│       └── inner() defined here (lexical child)       │
│            └── Looks up to find 'name'               │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Scope Chain

When accessing a variable, JavaScript searches up the scope chain.

const a = 'global a';

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

  function middle() {
    const c = 'middle c';

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

      // Scope chain lookup:
      console.log(d); // Found in inner scope
      console.log(c); // Found in middle scope
      console.log(b); // Found in outer scope
      console.log(a); // Found in global scope
    }

    inner();
  }

  middle();
}

outer();

Scope Chain Diagram

Variable lookup: a
       │
       ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  inner scope    │  Not found → go up
│  d = "inner d"  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         │
         ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  middle scope   │  Not found → go up
│  c = "middle c" │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         │
         ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  outer scope    │  Not found → go up
│  b = "outer b"  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         │
         ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  global scope   │  Found! āœ“
│  a = "global a" │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

What are Closures?

A closure is a function that remembers and can access its lexical scope even when executed outside that scope.

Simple Closure

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

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

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 'count' persists between calls!

Closure Visualization

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  createCounter() called                                │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │
│  │  count = 0                                        │ │
│  │                                                   │ │
│  │  returned function ─────────────────┐            │ │
│  │                                     │            │ │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ │
│                                        │              │
│  createCounter() finished              │ Closure      │
│  (but scope is NOT garbage collected) │ (remembers)  │
│                                        │              │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                                         │
                                         ā–¼
                        ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
                        │  counter()                  │
                        │  Still has access to count! │
                        ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Practical Closure Examples

1. Data Privacy (Encapsulation)

function createBankAccount(initialBalance) {
  let balance = initialBalance; // Private

  return {
    deposit(amount) {
      if (amount > 0) {
        balance += amount;
        return balance;
      }
    },
    withdraw(amount) {
      if (amount > 0 && amount <= balance) {
        balance -= amount;
        return balance;
      }
      return 'Insufficient funds';
    },
    getBalance() {
      return balance;
    },
  };
}

const account = createBankAccount(100);
console.log(account.getBalance()); // 100
account.deposit(50);
console.log(account.getBalance()); // 150
account.withdraw(30);
console.log(account.getBalance()); // 120

// Cannot access balance directly!
console.log(account.balance); // undefined

2. 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(5)); // 10
console.log(triple(5)); // 15
console.log(quadruple(5)); // 20

3. Memoization

function memoize(fn) {
  const cache = {}; // Closure over cache

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

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

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

const expensiveOperation = memoize((n) => {
  // Simulate expensive computation
  let result = 0;
  for (let i = 0; i < n * 1000000; i++) {
    result += i;
  }
  return result;
});

expensiveOperation(10); // "Computing..." (slow)
expensiveOperation(10); // "Cache hit!" (instant)

4. Event Handlers with State

function createToggle(element) {
  let isOn = false; // Private state

  element.addEventListener('click', function () {
    isOn = !isOn;
    element.textContent = isOn ? 'ON' : 'OFF';
    element.classList.toggle('active', isOn);
  });
}

// createToggle(buttonElement);

Closure Use Cases

1. Module Pattern

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

  function addToHistory(operation) {
    history.push(operation);
  }

  // Public API
  return {
    add(a, b) {
      const result = a + b;
      addToHistory(`${a} + ${b} = ${result}`);
      return result;
    },
    subtract(a, b) {
      const result = a - b;
      addToHistory(`${a} - ${b} = ${result}`);
      return result;
    },
    getHistory() {
      return [...history]; // Return copy
    },
  };
})();

Calculator.add(5, 3); // 8
Calculator.subtract(10, 4); // 6
console.log(Calculator.getHistory());
// ["5 + 3 = 8", "10 - 4 = 6"]

2. Partial Application

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

function greet(greeting, name, punctuation) {
  return `${greeting}, ${name}${punctuation}`;
}

const sayHello = partial(greet, 'Hello');
console.log(sayHello('Alice', '!')); // "Hello, Alice!"

3. Once Function

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

  return function (...args) {
    if (!called) {
      called = true;
      result = fn(...args);
    }
    return result;
  };
}

const initialize = once(() => {
  console.log('Initializing...');
  return 'Initialized!';
});

console.log(initialize()); // "Initializing..." then "Initialized!"
console.log(initialize()); // Just "Initialized!" (no re-run)

4. Debounce Function

function debounce(fn, delay) {
  let timeoutId; // Closure maintains timer

  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

const search = debounce((query) => {
  console.log('Searching for:', query);
}, 300);

// Rapid calls
search('a');
search('ab');
search('abc');
// Only "abc" will be searched (after 300ms)

5. Rate Limiter

function rateLimit(fn, limit) {
  let lastCall = 0;

  return function (...args) {
    const now = Date.now();

    if (now - lastCall >= limit) {
      lastCall = now;
      return fn(...args);
    }
  };
}

const limitedApi = rateLimit(() => {
  console.log('API called at', Date.now());
}, 1000);

Common Closure Pitfalls

1. Loop Variable Issue (var)

// āŒ Problem with var
for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i); // 3, 3, 3
  }, 100);
}

// āœ… Solution 1: Use let
for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i); // 0, 1, 2
  }, 100);
}

// āœ… Solution 2: IIFE (legacy)
for (var i = 0; i < 3; i++) {
  (function (j) {
    setTimeout(function () {
      console.log(j); // 0, 1, 2
    }, 100);
  })(i);
}

2. Accidental Closure Over Reference

// āŒ Problem: closure over object reference
function createHandlers() {
  const handlers = [];
  const obj = { value: 0 };

  for (let i = 0; i < 3; i++) {
    obj.value = i;
    handlers.push(() => console.log(obj.value));
  }

  return handlers;
}

const handlers = createHandlers();
handlers[0](); // 2 (not 0!)
handlers[1](); // 2
handlers[2](); // 2

// āœ… Solution: Create new object each iteration
function createHandlersFixed() {
  const handlers = [];

  for (let i = 0; i < 3; i++) {
    handlers.push(() => console.log(i));
  }

  return handlers;
}

3. Closure Over Wrong 'this'

// āŒ Problem
const obj = {
  name: 'MyObject',
  createLogger() {
    return function () {
      console.log(this.name); // undefined
    };
  },
};

// āœ… Solution 1: Arrow function
const obj2 = {
  name: 'MyObject',
  createLogger() {
    return () => {
      console.log(this.name); // "MyObject"
    };
  },
};

// āœ… Solution 2: Capture 'this'
const obj3 = {
  name: 'MyObject',
  createLogger() {
    const self = this;
    return function () {
      console.log(self.name); // "MyObject"
    };
  },
};

Memory Considerations

Closures can cause memory leaks if not managed properly.

Memory Leak Example

// āŒ Potential memory leak
function createHugeArray() {
  const hugeData = new Array(1000000).fill('data');

  return function () {
    // Entire hugeData is kept in memory
    // even though we only use length
    console.log(hugeData.length);
  };
}

// āœ… Fixed: Only capture what you need
function createHugeArrayFixed() {
  const hugeData = new Array(1000000).fill('data');
  const length = hugeData.length; // Capture only needed value

  return function () {
    console.log(length);
    // hugeData can be garbage collected
  };
}

Event Listener Cleanup

function setup(element) {
  const data = {
    /* large object */
  };

  function handler() {
    console.log(data);
  }

  element.addEventListener('click', handler);

  // Return cleanup function
  return function cleanup() {
    element.removeEventListener('click', handler);
  };
}

const cleanup = setup(element);
// Later:
cleanup(); // Removes listener, allows garbage collection

Best Practices

1. Minimize Closure Scope

// āŒ Capturing entire large object
function process(largeObject) {
  const data = largeObject;
  return () => data.value;
}

// āœ… Capture only what's needed
function processOptimized(largeObject) {
  const value = largeObject.value;
  return () => value;
}

2. Use let Instead of var in Loops

// āœ… Always use let in loops with async operations
for (let i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 100);
}

3. Clean Up When Done

function createResource() {
  const resource = heavyResource();

  return {
    use() {
      /* use resource */
    },
    dispose() {
      resource.cleanup();
      // Help garbage collector
    },
  };
}

4. Document Closure Behavior

/**
 * Creates a counter with private state.
 * @returns {Object} Counter with increment, decrement, getCount methods
 * @description Uses closure to encapsulate count variable
 */
function createCounter() {
  let count = 0; // Private, persists via closure
  // ...
}

Summary

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                 SCOPE AND CLOSURES                        │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                           │
│  SCOPE TYPES:                                             │
│  • Global - Accessible everywhere                         │
│  • Function - Accessible within function                  │
│  • Block - let/const within {}                           │
│                                                           │
│  DECLARATIONS:                                            │
│  • var   - Function-scoped, hoisted                      │
│  • let   - Block-scoped, TDZ                             │
│  • const - Block-scoped, TDZ, immutable binding          │
│                                                           │
│  CLOSURES:                                                │
│  • Function + its lexical environment                     │
│  • Remembers variables from parent scope                  │
│  • Persists even after parent function returns            │
│                                                           │
│  USE CASES:                                               │
│  • Data privacy/encapsulation                            │
│  • Function factories                                     │
│  • Memoization/caching                                    │
│  • Partial application                                    │
│  • Module pattern                                         │
│  • Event handlers with state                              │
│                                                           │
│  PITFALLS:                                                │
│  • var in loops with async                               │
│  • Closure over references (not values)                   │
│  • Memory leaks (unused closures)                         │
│                                                           │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Next Steps

  • •Practice with the examples in examples.js
  • •Complete the exercises in exercises.js
  • •Learn about higher-order functions
  • •Explore the module pattern in depth
.6 Scope Closures - JavaScript Tutorial | DeepML