Understanding Closures in JavaScript
A closure is one of the most frequently misunderstood concepts in JavaScript — and one of the most frequently used. Understanding it starts with lexical scope.
A closure is one of the most frequently misunderstood concepts in JavaScript, and also one of the most frequently used, often without the developer being aware of it. Understanding closures requires first understanding lexical scope.
Lexical Scope
Lexical scope means that the scope of a variable is determined by where it is written in the source code, not where it is called from at runtime. A function defined inside another function has access to the variables of its parent function.
function outer() {
const message = 'hello';
function inner() {
console.log(message);
}
inner();
}
outer(); // hello
Here, inner can access message because message is declared in the enclosing scope. This is lexical scope. The inner function reads up the scope chain to find the variable.
What a Closure Is
According to the MDN Web Docs, a closure is the combination of a function bundled together with references to its surrounding state, the lexical environment. A closure gives a function access to its outer scope. In JavaScript, closures are created every time a function is created, at function creation time.
The critical behaviour of a closure is that the inner function retains access to the outer function’s variables even after the outer function has finished executing.
function outer() {
const name = 'John';
function inner() {
console.log(name);
}
return inner;
}
const fn = outer();
fn(); // John
outer has finished executing. Its local scope would normally be discarded. Because inner was returned and holds a reference to name, the variable is retained in memory. That retained reference is the closure.
A Practical Example
Closures are commonly used to create functions with private, encapsulated state.
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1(); // 1
counter1(); // 2
counter2(); // 1
Each call to createCounter produces a new closure with its own independent count variable. counter1 and counter2 do not share state. The variable count is not accessible from outside the returned function, which is the equivalent of a private variable in JavaScript.
The Difference Between Lexical Scope and a Closure
Lexical scope describes the rule by which a nested function can read variables from its parent scope. A closure is what forms when that inner function is returned or otherwise used outside of its parent scope, keeping those variables alive beyond the parent function’s execution. Lexical scope is the mechanism, closures are the result.
What to Do Now
Run the counter example above. Then verify that count is not accessible directly:
const counter = createCounter();
counter(); // 1
console.log(count); // ReferenceError: count is not defined
The variable exists and is maintained across calls, but it is not reachable from the outside. That behaviour (state persisted privately inside a function) is the core utility of a closure.