1.1 Overview
In information technology, a closure (also lexical closure or function closure) is a technique for implementing lexically scoped name binding in programming languages. A closure is typically a function object that retains access to variables from its enclosing lexical scope even after that scope has finished execution. Closures are fundamental to functional programming paradigms and are widely used in languages such as JavaScript, Python, Ruby, and Lisp, enabling features like data hiding, currying, and callback mechanisms.
1.2 Historical context
The concept of closures originated in the 1960s with the development of the Lisp programming language, particularly in the context of lexical scoping introduced by Scheme in the 1970s. Peter J. Landin’s SECD machine and his work on the lambda calculus provided a theoretical foundation. The term “closure” was coined by John McCarthy and later formalized by computer scientists such as Guy Steele and Gerald Sussman. Closures became a standard feature in many languages with the rise of functional programming and widespread adoption in JavaScript, Python, and other mainstream languages during the 1990s and 2000s.
1.3 Types of closures
1.3.1 Lexical closures
A lexical closure captures the environment at the time the function is defined. It binds variables by their lexical (static) scope, meaning the closure accesses the variables that were visible in the source code at the point of definition. Most modern languages implement lexical closures, as they align with static scoping rules and enable predictable behavior.
1.3.2 Dynamic closures
Dynamic closures capture the environment at the time the function is called, based on the dynamic call stack. They are less common and appear in languages with dynamic scoping, such as older Lisp dialects. Dynamic closures can lead to non‑intuitive behavior because the referenced variables depend on runtime calling context. Most contemporary languages favor lexical closures.
2 Implementation mechanisms
2.1 Environment model
In typical implementations, a closure is represented as a combination of the function’s code and a reference to the lexical environment (a mapping of variable names to values) at the time of definition. When the closure is invoked, it uses this captured environment to resolve free variables. This model is central to the semantics of lexically scoped languages.
2.2 Representation in memory
2.2.1 Stack vs heap allocation
Local variables of a function are normally allocated on the call stack. However, variables that are captured by a closure cannot be stored solely on the stack because the closure may outlive the function’s execution. Implementations therefore allocate such variables on the heap (or use a combination of stack frames and heap‑allocated “closure objects”). The language runtime decides allocation based on escape analysis.
2.2.2 Captured variables and upvalues
Each captured variable is often stored in a heap‑allocated cell called an “upvalue” (a term used in Lua and other languages). The closure holds references to these upvalues rather than to the original stack slots. When multiple closures capture the same variable, they share the same upvalue, ensuring mutations are visible across all closures.
2.3 Garbage collection considerations
Since closures can hold references to heap‑allocated environments, they may prevent the garbage collector from reclaiming those variables until the closure itself becomes unreachable. Language runtimes must trace closure references correctly, and cyclic dependencies between closures and environments must be handled (e.g., through weak references or cycle detection). Improper management can lead to memory leaks, especially in long‑lived closure‑heavy applications.
3 Common use cases
3.1 Data encapsulation
Closures provide a way to create private state. By defining variables inside a function and returning an inner function that accesses them, the outer variables become inaccessible from outside, simulating private members. This pattern is often used to implement modules or objects without a native class system.
3.2 Partial application and currying
Closures enable partial application, where a function is called with fewer arguments than it expects, returning a new function that awaits the remaining arguments. A curried function transforms a multi‑argument function into a chain of single‑argument closures. These techniques are common in functional programming and are supported by closures that capture the already‑provided arguments.
3.3 Event handlers and callbacks
In event‑driven programming (e.g., browser JavaScript), closures are used as callbacks that retain access to the surrounding context. When an event handler is registered, it can reference variables from the enclosing function, even after that function has returned. This ability is essential for asynchronous programming.
3.4 Iterators and generators
Closures can maintain iteration state across multiple invocations. For example, a closure can hold an index or a pointer into a data structure, advancing it each time the closure is called. Many languages implement generators (e.g., Python’s generator functions) using closures that preserve the execution state between yields.
3.5 Memoization and caching
A closure can store a cache (e.g., a dictionary) that persists across calls. The closure checks the cache before computing a result, thus improving performance for repeated inputs. This pattern is a straightforward application of data encapsulation and state retention provided by closures.
4 Language‑specific details
4.1 Closures in JavaScript
4.1.1 Scope chains
JavaScript creates a scope chain at function definition time. Each function has a reference to its outer lexical environment. When a closure is created, it retains the entire scope chain, not just the variables it uses. This can cause memory overhead if large outer scopes are retained unnecessarily.
4.1.2 Closures and the module pattern
The module pattern in JavaScript leverages closures to create private data and expose a public API. An immediately invoked function expression (IIFE) returns an object containing methods that close over the private variables. This was a common design pattern before ECMAScript 6 introduced native modules.
4.1.3 Pitfalls with loops
A well‑known issue occurs when closures are created inside a loop using var. Because var is function‑scoped, all closures capture the same variable, leading to unexpected behavior (e.g., all callback functions see the final iteration value). The remedy is to use let (which provides block scoping) or to create a new scope per iteration via an IIFE.
4.2 Closures in Python
4.2.1 Nonlocal keyword
Python supports closures, but by default nested functions can only read, not assign to, outer non‑global variables. To modify a captured variable, the nonlocal declaration is required. Without it, an assignment creates a new local variable instead of updating the captured one.
4.2.2 Late binding behavior
Python closures exhibit late binding: the closure captures the variable *name*, not the value at definition time. As a result, when closures are created in a loop, they all refer to the same variable, and by the time they are called, the variable holds the last iteration’s value. This is analogous to JavaScript’s loop pitfall with var. Developers typically use default arguments or partial to bind the current value.
4.3 Closures in Ruby
4.3.1 Blocks, procs, and lambdas
Ruby provides closures via blocks, procs, and lambdas. Blocks are syntactic closures attached to method calls; they capture the surrounding lexical scope. Procs and lambdas are first‑class closure objects that can be stored and passed. The main difference is that lambdas check argument arity, while procs do not.
4.3.2 Binding objects
Ruby exposes Binding objects that encapsulate the execution context (scope) at a given point. A closure can be created using Proc.new in conjunction with a binding, but typically closures are formed automatically via blocks. The binding method allows introspection and evaluation of code within a specific scope.
4.4 Closures in Lisp dialects
4.4.1 Scheme lexical scoping
Scheme was one of the first languages to implement lexical closures as a core feature. In Scheme, all procedures are closures; they capture the environment of their definition. The language fully supports higher‑order functions and uses closures for control structures like call/cc (continuations).
4.4.2 Common Lisp closures
Common Lisp supports closures via lexical closures defined with lambda. Variables are captured by reference, and closures can be created inside loops without the late‑binding pitfalls if the variable is allocated in a new scope per iteration (e.g., using let inside the loop). Common Lisp also provides dynamic closures through special variables (dynamic scoping) but lexical closures are the norm.
5 Performance and optimization
5.1 Inlining and escape analysis
Compilers and just‑in‑time (JIT) engines may inline small closures to eliminate the overhead of a function call. Escape analysis determines whether a closure outlives its enclosing scope; if it does not, the captured variables can be stack‑allocated. In cases where the closure does not “escape,” the compiler can optimize away the closure entirely.
5.2 Closure conversion
Closure conversion (also called “lambda lifting”) is a compilation technique that transforms closures into ordinary functions with explicit environment parameters. The captured variables are passed as additional arguments. This lowers the abstraction level and allows further optimization, though it may introduce extra function calls.
5.3 Trade‑offs in memory usage
Closures incur memory overhead due to the need to store captured environments on the heap. Each closure retains references to its entire lexical environment, sometimes including unused variables. This can increase memory consumption, particularly when many closures are created in tight loops. Developers can mitigate this by limiting the scope of captured variables and using tools like weak references.
6 Alternatives and related concepts
6.1 First‑class functions
First‑class functions are functions that can be treated as values (assigned to variables, passed as arguments, returned from other functions). Closures are a natural extension of first‑class functions: a first‑class function that captures its environment becomes a closure. Not all first‑class functions are closures (e.g., pure functions with no free variables), but closures rely on first‑class function support.
6.2 Anonymous functions (lambda)
Anonymous functions (also called lambdas) are function definitions without a name. They are often used to create closures on the fly. While lambdas are not closures per se, languages typically allow lambdas to capture variables, making them closures. The terms are sometimes conflated in casual usage.
6.3 Partial application vs. closures
Partial application is the process of fixing some arguments of a function to yield a new function. Closures are the mechanism that makes partial application possible (the captured arguments are stored in the closure’s environment). However, partial application can also be achieved without closures if the language provides explicit currying or function‑binding methods (e.g., Function.prototype.bind in JavaScript uses closures internally).
6.4 Object‑oriented patterns
In object‑oriented programming, objects with a single method (e.g., a Runnable interface) serve a similar role to closures: they bundle behavior with state. A closure can be viewed as a lightweight object with one method, and an object can be seen as a closure with multiple methods. The “Command” and “Strategy” design patterns often use closures instead of separate classes.
7 Controversial issues (avoided per guidelines)
Per editorial policy, this article does not address contemporary controversial political, religious, ethnic, or territorial issues. The discussion is limited to the technical and historical aspects of closures in computer programming.