Closures and Scope¶
A closure is a function that retains a reference to variables from its outer function's scope even after the outer function has finished executing. The closure is a data structure stored on the heap inside the JS runtime - only the inner function can read/write to it. Understanding scope and closures is essential for encapsulation, factory functions, and module design in Node.js.
Key Facts¶
- Scope (visibility area): viewed from an identifier's perspective - where it is visible (function, block, module, global scope)
- Lexical context/environment: viewed from a position in the program - which identifiers are accessible at that point
- A closure is a heap data structure; there is no external reference - only the inner function can access it
- Similar to
thisin objects, but unlikethis, you cannot add fields to a closure from outside
Three Conditions for a Closure¶
All three must be present simultaneously: 1. An outer function defining variables 2. An inner function referencing those variables 3. The inner function being returned or passed outside
If any part is missing, it is not a full closure. Accessing a global variable from a function is NOT a closure.
Node.js Module Scope¶
- Each module is wrapped in a function, creating module-level scope - no true global scope for user code
- In browser JS, all files share one scope unless wrapped in IIFEs or using module bundlers
Patterns¶
Closure-Based Encapsulation¶
function createCounter(initial = 0) {
let count = initial; // only accessible through returned methods
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; },
};
}
const counter = createCounter(10);
counter.increment(); // 11
// counter.count → undefined (truly private)
Block Scope as Inheritance¶
function processUser(user) {
let greeting = 'Hello'; // default "base" behavior
if (user.isVIP) {
let greeting = 'Welcome, esteemed'; // "override" in nested scope
console.log(`${greeting} ${user.name}`);
return;
}
console.log(`${greeting} ${user.name}`);
}
Module Wrapping (How Node.js Creates Scope)¶
// Node.js wraps each module:
// (function(exports, require, module, __filename, __dirname) {
// ... your module code ...
// });
// This is why top-level variables are module-scoped, not global
Gotchas¶
- Closures retain references to the entire lexical environment, not just used variables - can cause memory retention
- In loops with
var, all closures share the same variable; uselet(block-scoped) for per-iteration scope - Accessing a global variable from a function is NOT a closure - confusing these leads to architectural misunderstandings
See Also¶
- v8 optimization - how V8 stores closure data structures on the heap
- modules and packages - module wrapping creates the enclosing scope
- design patterns gof - closures as foundation for many patterns
- solid and grasp - closure-based immutable records