Without any fancy intro, let's jump directly to what closure
is.
Simply put, Closure is an inner function that remembers the environment it was created in.
Think of it like an aware function has access to the values (and parameters) from an outer function.
What makes a closure powerful is that it is capable of reading and manipulating the data of its outer functions.
Here's a very simple example of a closure.
Think of it as a code from an app. Our goal is to prompt the user to rate the app on every 3rd visit.
function promptRating() {
var appUsage = 0;
return function() {
appUsagae++;
if (appUsage % 3 === 0) console.log('Please rate the app.');
}
};
promptRating
is a function that returns an inner function. This inner function is a closure. It remembers and has access to the variable appUsage
from the outer function.
To see it in action:
// Init the outer function
var prompt = promptRating();
// Call `prompt` in regular workflow.
// If this is a multiple-of-3 visit, the user will be prompted to rate the app.
prompt(); // No Output
prompt(); // No Output
prompt(); // Output: Please rate the app.
Being so simple yet powerful has its trade-offs: most notably when creating closures inside loops. Remember that closures have access to the data of the outer function.
So in a loop based on i
, the code inside the closure will execute based on the current value of i
. Not the old value of i
which existed when the closure was created.
Here's a simple code to explain this:
function arrayOfNums(num) {
var output = [];
for (var i = 0; i < num; i++) {
// Closure being pushed into the output array:
output.push(function() { return i; });
}
return output;
}
var arr = arrayOfNums(3);
arr[0](); // Output: 3
arr[1](); // Output: 3
arr[2](); // Output: 3
Another gotcha instance would be creating closures inside a timeout/interval. When run, the code inside the closure will execute based on the current data of the outer function. The values of this data may have gone stale before the timeout was met.
Here's a simple code to explain this:
function countdown(upto) {
for (var i = 0; i < upto; i++) {
// Closure set to run after 1000ms
setTimeout(() => console.log(i), 1000);
}
};
countdown(5); // Output: 5 5 5 5 5
In conclusion, closures are simple beings. It is always an inner function which has access to the outer function scope.
If the outer function is called multiple times, every call creates a new closure. Closure's existence is dependent on their parent function's existence. Like I said, simple beings.
Top comments (2)
You can mention binding as well.
let
has lexical scope binding (e.g. it binded insidefor
), where isvar
has scope of function body:Or using
bind
:Top tip, using markdown codeblocks where ''' is backtick
''' js
// Will give you syntax highlighting
'''