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.
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!"
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
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"); };
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.
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.