Glossary / Function scope
Function scope
A function invocation creates an environment for its parameters and local declarations. Those bindings are ordinarily inaccessible outside the function, which lets a function encapsulate intermediate state and avoid polluting surrounding scopes. Each call gets its own activation environment, even when the same function is called recursively or concurrently through asynchronous workflows.
Function environment behavior
Parameters, var declarations, and function declarations belong to function-level environments. let and const declared inside the function additionally respect nested block scopes. A function can read outer lexical bindings, and nested functions can retain those bindings through closures after the original call returns.
function makeCounter() {
let count = 0;
return () => ++count;
}
const next = makeCounter();
next(); // 1
next(); // 2; the closure retains this invocation's binding
A closure keeps the relevant environment reachable; it does not keep every local variable alive if the engine can prove a binding is not needed, but code should reason about references it explicitly retains. Avoid treating function scope as a security boundary: returned references and mutable objects can still expose internal state.
Function parameters are local bindings, but object arguments are references to the same object. Reassigning the parameter does not reassign the caller’s variable; mutating a referenced object is observable by the caller. This distinction is a common source of bugs.
Interview questions
- Does each call have its own local variables? Yes, each invocation has its own environment and parameter bindings.
- Can a local outlive the call? Yes, a closure can retain access to it.
- Does reassigning an object parameter replace the caller’s variable? No, but mutating the shared object is visible to the caller.
- Is function scope the same as block scope? No;
varis function-scoped while lexical declarations are block-scoped.