You know what? In JavaScript, all functions are naturally closures (with only one exception). Really, let’s go through to understand how this works.
Closure are functions bundled together with its lexical references. While that’s the formal definition, what do closures really mean? Before diving into closures, understanding first-class functions is beneficial, as it provides foundational knowledge that helps you grasp how closures work.
Key Concepts to Know First:
1. First-class functions: Functions that can be assigned to variables, passed as an argument and returned from functions.
2. Function scope: Functions can access variables from their own scope and from the surrounding (outer) scope.
3. Execution context: Each time a function is invoked, a new execution context is created. This includes the variable environment and the scope chain.
In general, variables created within a function are scoped to that function and are destroyed immediately after the function completes execution. For example, consider a function named premiumContentAccess that contains two variables: articlesAllowedToAccess and currentlyAccessed.
function premiumContentAccess() {
let articlesAllowedToAccess = 3;
let currentlyAccessed = 0;
}
premiumContentAccess();
When premiumContentAccess is executed, the local variables articlesAllowedToAccess and currentlyAccessed are created. Once the function execution completes, these variables go out of scope and are eligible for garbage collection, meaning they are effectively destroyed and cannot be accessed afterward.
Is it possible to retain access to these variables even after the execution of premiumContentAccess? Here comes the closure.
What is Closure?
A closure is formed when a function is returned (or created) that has access to its surrounding (outer) variables. These variables references are bundled together with the function, allowing them to persist.
function premiumContentAccess() {
let articlesAllowedToAccess = 3;
let currentlyAccessed = 0;
function accessArticle() {
if (currentlyAccessed >= articlesAllowedToAccess) {
return 'Premium Article Access Reached';
}
currentlyAccessed++;
return 'Article accessed';
}
return accessArticle;
}
let accessArticle = premiumContentAccess();
console.log(accessArticle()); // Outputs: Article accessed
console.log(accessArticle()); // Outputs: Article accessed
console.log(accessArticle()); // Outputs: Article accessed
console.log(accessArticle()); // Outputs: Premium Article Access Reached
Breakdown of the Example
1. Outer Function: premiumContentAccess() is the outer function that defines the variables articlesAllowedToAccess and currentlyAccessed.
2. Inner Function: accessArticle is the inner function that accesses the variables from its lexical scope, which is defined in the outer function.
3. Closure: When premiumContentAccess() is called, it returns the accessArticle function retaining access to the variables articlesAllowedToAccess and currentlyAccessed, even after the outer function has finished executing.
4. State Retention: The closure allows accessArticle to maintain its own state regarding how many articles have been accessed. Each call to accessArticle modifies currentlyAccessed, which is preserved due to the closure.
All Functions Are Closures:
As we mentioned at start, all functions are naturally closures.
That is: they automatically retain a reference to their creation context through a hidden [[Environment]] property, allowing their code to access outer variables.
Exception: Functions created using the new Function constructor set the [[Environment]] to the global context instead of the creation context.
Thank you for reading! I hope you found this blog informative and engaging. If you notice any inaccuracies or have any feedback, please don’t hesitate to let me know.
Top comments (2)
A couple of problems with this:
You kind of stated these things in other parts of the post, but confused them with this statement.
Thank you for your feedback! You are absolutely right that a closure is created whenever a function is created, but the key detail is that the function must have access to variables from its outer (lexical) scope.
So, while the function doesn't necessarily have to be returned from another function, it still needs to 'capture' its surrounding variables, which is what forms the closure.
I'll also update the original statement to clarify this: 'A closure is formed when a function is returned (or created) that has access to its surrounding (outer) variables.' This revision better reflects that closures can be formed both when a function is returned from another function, or simply created within a scope.