The point of a closure is to create an outer function that can act like a factory. This factory function produces other smaller functions (an inner function inside the outer factory function). Now before I go on, let's remember that the inner function (against intuition and assisted by the language itself) IS TOTALLY allowed to safely use the local state of the outer function. This works properly even when the outer function's state is removed from the stack, and disappears.
This is the key to closures, and to take proper advantage of it we really only want to call the factory function (outer function) when we want to produce an inner function that depends on a local state that the outer function provides. So let's use a suuuppper simple example.
Consider this JavaScript code:
var outerFunction = function(outerFunctionState) {
var innerFunction = function() {
console.log("Snapshot of the outerFunction's state is: " + outerFunctionState);
}
return innerFunction;
}
var generatedFunction1 = outerFunction(5);
var generatedFunction2 = outerFunction(10);
generatedFunction1();
generatedFunction2();
Realize that the output will be:
Snapshot of the outerFunction's state is: 5
Snapshot of the outerFunction's state is: 10
So really we've generated two functions that simply print 5 and 10 respectively...big woop. But under the hood its important to realize we've actually generated two functions that differ slightly depending on the input we provide their factory with.
Now consider the following example where we make the outerFunctionState
equal to the current time that we pass in. We then can access the passed in time as well as the current time from the inner function:
Consider the following code:
var outerFunction = function(outerFunctionState) {
var innerFunction = function() {
console.log("Time difference: " + (Date.now() - outerFunctionState)/1000); // Convert from MS => seconds
}
return innerFunction;
}
var generatedFunction1 = outerFunction(Date.now());
// Hardly any time has passed since we've generated our function
generatedFunction1();
// Artificial time delay
for (var i = 0; i < 1000000000; ++i) {}
for (var i = 0; i < 1000000000; ++i) {}
// But when we call it later on we get the difference of the current time
// and the outerFunctionState which is the time when we first generated
// the function
generatedFunction1();
Notice how in the above example, we can call generatedFunction1()
at any later time to get a feel for how long that function has been in existance. Pretty cool since we only had to pass in the time it was when we created it.
Now if we wanted to create a more functional timer
out of it we can force the outerFunctionState
to be a variable inside the outerFunction
and just set that equal to the current time right when the outerFunction
was executed. Now the code will generate a stop-watch that tells you how long its been since it was first generated.
Consider the following example:
var stopWatchBuilder = function() {
var outerFunctionState = Date.now();
var innerFunction = function() {
console.log("Time difference: " + (Date.now() - outerFunctionState)/1000);
}
return innerFunction;
}
var timer = stopWatchBuilder();
// Next to no time at all from when we generateFunction1 and call it
timer();
// Artificial time delay
for (var i = 0; i < 1000000000; ++i) {}
for (var i = 0; i < 1000000000; ++i) {}
for (var i = 0; i < 1000000000; ++i) {}
// But when we call it later we get the difference of the current time
// and the outerFunctionState which is the time when we first generated
// the function
timer();
for (var i = 0; i < 1000000000; ++i) {}
for (var i = 0; i < 1000000000; ++i) {}
// Again!
timer();
It is important to realize we want to return the ability to call some inner function that depends on some local state. Next, we only want to call the outerFunction (builder factory) when we absolutely have to. So the stopWatchBuilder/stopWatchFactory in my example only gets called once but its inner function is something you may want to put in a while loop to see the time spread increasing.