Function Closure & Function Hoisting

Interactive tutorial — examples, notes, and runnable demos
Author: Dr. Khaled Alrfou
Topic: JavaScript fundamentals

1 — Function Closures

A closure is a function that remembers variables from its outer (enclosing) scope even after that outer function has finished executing. Closures are used for data encapsulation, private state, and creating persistent state between calls.

Key uses
private variables · data hiding · counters & state · event handlers

Simple closure example

// outer returns an inner function that uses outerVar
function outer() {
  let outerVar = "I'm outside!";
  function inner() {
    return outerVar;
  }
  return inner;
}

const f = outer();
console.log(f()); // "I'm outside!"
Note: Each time `outer()` is called it creates a fresh closed-over environment. The returned inner function retains access to that environment.
Output:
— run the example to see results —
Speaker notes — Closure example

Explain that closures "remember" the lexical environment where they were created. Use the greeting example to show how each closure keeps its own name value.


Practical closure: counter factory

// counter uses closure to keep count private
function makeCounter() {
  let count = 0;
  return {
    inc: function() { count += 1; return count; },
    dec: function() { count -= 1; return count; },
    value: function() { return count; }
  };
}

const c = makeCounter();
console.log(c.inc()); // 1
console.log(c.inc()); // 2
console.log(c.value()); // 2
Output:
— run the example to see results —
Speaker notes — Counter

Show how `count` is not directly accessible from outside; it is private. The returned object exposes methods that close over `count` so its state persists across calls.

2 — Function Hoisting

Hoisting is JavaScript's behavior of moving function declarations (and variable declarations) to the top of their containing scope during the compilation phase. This lets you call declared functions before their appearance in source code. Function expressions (functions assigned to variables) are not hoisted in the same way.

Hoisting: declaration vs expression

// Function declaration — hoisted
sayHello(); // works (declaration is hoisted)
function sayHello() {
  console.log("Hello from declaration");
}

// Function expression — not hoisted
try {
  greet(); // ReferenceError or TypeError depending on var/let/const
} catch(e) {
  console.error("Calling greet before assignment fails:", e.message);
}
const greet = function() { console.log("Hello from expression"); };
Output:
— run the examples to see results —
Speaker notes — Hoisting

Explain compilation vs runtime, and emphasize that function declarations are hoisted while function expressions (especially when using const/let) are not initialized before assignment and will throw if called early.


Interactive hoisting demo (safe)

Click the buttons to run small snippets inside a safe sandbox (via eval inside a try/catch). The demo displays what would happen when a call is placed before the declaration.

— nothing yet —

3 — Closure vs Hoisting: Quick Comparison

  • Closure: runtime concept — functions retain access to lexical scope variables even after the outer function finishes.
  • Hoisting: compile-time behavior — declarations are moved to top of their scope so they can be referenced earlier.
  • Use closures for encapsulation and private state. Use hoisting carefully — it can help organization but may be confusing for readers.
Tip: Prefer readable order and explicit exports. Use closures intentionally (e.g., factories, modules). Avoid relying on hoisting to hide confusing ordering.
Want slides or a downloadable .pptx of this tutorial? Use the download link in the footer or ask me to generate a .pptx.