Yup. Functions are first class objects. They can be stored in variables, passed around, and even given custom properties just like any other object. In fact, they’re like other objects in just about every other way except you also get a special behavior if you put ()
after their identifiers which invokes the code stored in the function’s body.
var obj = {};
function fun () {}
var temp;
temp = obj;
temp = fun; // OK
console.log(obj);
console.log(fun); // OK
obj.myValue = 1;
fun.myValue = 1; // OK
fun();
obj(); // NOT OK
What makes a function a closure is when it uses 1 or more [non-global] variables within its function body that aren’t a part of that function’s parameters or otherwise declared within the function itself. The getDelay
function in your example does this with the startTime
variable. startTime
is declared in stopWatch
, outside of getDelay
, yet getDelay
’s function body reaches out and accesses it to use for itself. When it does this, it creates a reference to that variable and can access it whenever its called.
Closures then become similar to objects with special “properties” that exist as references to other variables. stopWatch
creates a getDelay
function that has a kind of startTime
property associated with it, though its hidden from you the user because its not actually a property… though it could have been. Consider stopWatch
written another way, since, after all, we just saw how functions can be given properties like objects:
function stopWatch () {
function getDelay() {
var elapsedTime = Date.now() - getDelay.startTime;
alert(elapsedTime);
}
getDelay.startTime = Date.now();
return getDelay;
}
This works the same way, only the variable we’re using to capture the startTime
is assigned directly to the getDelay
function. This also means its accessible in timer
, the variable getDelay
is assigned to when stopWatch()
is called.
var timer = stopWatch();
alert(timer.startTime); // alerts startTime value set in stopWatch
The closure version doesn’t expose startTime
as a property, though. Its simply an internal reference to the original variable from whatever scope it was originally defined. In stopWatch
, its the local scope of the stopWatch()
call. Normally local variables in a function call are destroyed when the function completes and returns, but since getDelay
hangs on to startTime
as a closure variable, it gets to live as long as getDelay
(assigned to timer
) does.
Ultimately, this becomes the purpose of the stopWatch
function. Its a factory creating startTime
variables and getDelay
functions that access them. Each time its called, new versions of that variable and function are created, and though only the function is returned, the variable is still being used because of the closure reference that the function keeps to it.
Closures can get tricky, and it may take a while to get your head around them. The important bit is that closure variables are references not copies. Often people assume the value of a closure variable gets baked into a function, but thats not true. The value of the variable is accessed when the function is run, not created. This will often confuse people when creating functions in loops.
var funcs = [];
for (var i = 0; i < 3; i++) {
function alertLoopValue () {
alert(i);
}
funcs.push(alertLoopValue);
}
var lastFunc = funcs[2];
lastFunc(); // what's alerted?
At first glance, it looks like the obvious behavior here is that funcs[0]
would alert 0, funcs[1]
would alert 1, and funcs[2]
would alert 2. But truth is, lastFunc
, as well as all of the other functions in the funs
array would alert 3. This is because, when you call them, them loop has already run through and incremented the i
variable to 3, the first value that fails the loop condition of i < 3
. And since closure variables are references, not copies, the value of i
when the function is called gets used, not when it was created in the loop. There’s only one i
variable, and every func
function references that same variable and at this point, after the loop, that variable’s value is 3.
…It takes time. 