Closure
You can think of closure as a way to “remember” and continue to access a function’s scope (its variables) even once the function has finished running.
In JavaScript, a closure is a function that has access to the variables and parameters of its outer (enclosing) function, even after the outer function has finished executing. This means a closure gives you access to an outer function's scope from an inner function.
Closures are created every time a function is created, at function creation time. They are a fundamental aspect of the JavaScript language and are crucial for understanding certain programming concepts and techniques, especially in asynchronous programming and functional programming.
Here's an example of a closure:
function outerFunction() {
var outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable); // Accesses variable from outer function
}
return innerFunction;
}
var myInnerFunction = outerFunction();
myInnerFunction(); // Logs: 'I am outside!'
In this example, outerFunction
returns innerFunction
. Even though outerFunction
has finished executing and its execution context is gone, innerFunction
still has access to outerVariable
because a closure was created.
Closures have several practical uses in JavaScript, such as:
- Data privacy: Any variable defined inside the outer function can only be accessed by the inner function and is hidden from the rest of the code.
- Emulating private methods: In object-oriented programming languages, there's a concept of private methods, which are methods that can't be called from outside a class. JavaScript doesn't have this feature built-in, but it can be emulated using closures.
- Creating function factories: A function factory is a function that returns another function, and the returned function can have access to the factory's parameters.
- Implementing advanced patterns like decorators, promises, and currying.
It's important to note that closures can lead to over-consumption of memory if not handled properly, as some variables might not get garbage collected as expected. Therefore, while closures are powerful, they should be used judiciously.
Modules
The most common usage of closure in JavaScript is the module pattern. Modules let you define private implementation details (variables, functions) that are hidden from the outside world, as well as a public API that is accessible from the outside.
function User(){
var username, password;
function doLogin(user,pw) {
username = user;
password = pw;
// do the rest of the login work
}
var publicAPI = {
login: doLogin
};
return publicAPI;
}
// create a `User` module instance
var fred = User();
fred.login( "fred", "12Battery34!" );
The User() function serves as an outer scope that holds the variables username and password, as well as the inner doLogin() function; these are all private inner details of this User module that cannot be accessed from the outside world.
Executing User() creates an instance of the User module—a whole new scope is created, and thus a whole new copy of each of these inner variables/functions. We assign this instance to fred. If we run User() again, we’d get a new instance entirely separate from fred.
The inner doLogin() function has a closure over username and pass word, meaning it will retain its access to them even after the User() function finishes running.
publicAPI is an object with one property/method on it, login, which is a reference to the inner doLogin() function. When we return publicAPI from User(), it becomes the instance we call fred.
At this point, the outer User() function has finished executing. Normally, you’d think the inner variables like username and password have gone away. But here they have not, because there’s a closure in the login() function keeping them alive.
That’s why we can call fred.login(..)—the same as calling the inner doLogin(..)—and it can still access username and password inner variables.