In this article, you will cover the basics of what a closure is and why JavaScript benefits from closures.
If you would like to follow along with this article, you will need:
A closure can be defined as a persistent scope that is held onto by a variable. Languages like JavaScript and Ruby support closures, which allows variables to have references to their parent scope even after their programming block has been executed and, as long as these variables have a reference somewhere, the closure exists.
Consider the following closure example:
function outerFunction() {
let delayed = veryLongOperation();
let innerFunction = function() { someCallback(delayed); }
}
The innerFunction
is a function that is able to access its parent scope elements - like the delayed
object.
Now that we’ve seen what closures are, why are they useful?
JavaScript can be asynchronous when it comes to execution. When an operation is completed, a callback has to be employed. This callback now has to run with the same execution context as its caller/parent. This means that whatever variables or functions were available to the parent must now be made available to the callback as well. If these callbacks did not have closures, then we’d have to manually bind the needed scope members.
Closures make our lives a lot easier by handling this for us in the background. If there is a function inside a function, the inner function has access to its scope members throughout its lifetime.
Consider the following class definition example:
let classExample = function () {
let randomNumber = Math.floor(Math.random() * 10);
return function() {
console.log(randomNumber);
}
}
The scope chain of the inner function is extended to include the members of the classExample
function.
Another instance where closures come into play is during currying. Currying is the process of returning multiple functions to reduce arity. It’s employed in functional programming paradigm to reduce state changes.
Closures can be used along with currying to introduce private members for JavaScript classes.
function curryingExample() {
let message = "Hello.";
return {
getMessage: function() { console.log('private message', message); }
};
}
Create a new curryExample()
instance:
let curry = new curryingExample();
Attempt to access message
:
console.log('undefined message', curry.message);
When console.log(curry.message);
executes the value will be undefined
.
Attempt to use getMessage()
:
curry.getMessage();
When curry.getMessage();
executes, the value will be "Hello."
.
Closures are a very subtle yet powerful feature of JavaScript and understanding them is a very important step on the path to becoming a serious JavaScript developer.
This is explained in more detail in Kyle Simpson’s excellent write-up on Closures.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!