Understanding JavaScript Closures [con't]
Emulating Static Variables
Variables that are created within a closure have a lot in common with static variables found in languages like Visual Basic, in that they also retain their value between function calls. Static variables can be quite useful because they offer the security of being local variables while maintaining persistence of global ones. Closures can be used to mimic static variables because changes in the enclosed variables' values are retained between calls, as the following example demonstrates:
The above code is a variation of the basic pattern presented above in that
the factory is called as an inline function to set a variable to the function
of the same name (increment
).
A limitation of the basic closure pattern is that it doesn't allow much flexibility in how the local variables are used. Moreover, since the enclosed function is a private member of the outer function, it can only be used in a sort of "black box" kind of way. For instance, the previous example was used to increment a variable from zero. If you wanted to start at, say one, instead, or wanted to decrement, you were out of luck. The next example provides more flexibility by accepting both a function and a variable to work with. That way we can pass in our own incrementor function, static variable, and initial value:
staticVar
variable must not be defined until the createStaticVariable()
function is called. Therefore, we have to get the JavaScript interpreter to
evaluate the function code within the outer function in order to create the
proper lexical scoping environment. This is accomplished with the help of the
eval()
function.
We can make the createStaticVariable()
function available to all functions
by adding it to the Function
object's prototype
. We can now dispense with
the first argument:
Notice that the fc
variable requires the var
keyword in order to make it local.
Without it, the fc
variable would be appended to the global window
object instead! The
customIncrementer()
function specifically checks the window
object for the staticVar
,
which should be undefined
.
Using Closures in Loops
Functions called from within a loop can highlight variable discrepancies between
creation and execution time when global variables are referred to within dynamically
created functions. The function below stores a number of functions in an array
to be called later. The outer incrementor1
variable is accessed from within
the inner anonymous function, so one would expect that the variable's value
would be preserved until we called the function. In fact, the function shows
that incrementor1
is three (3), that is the value that it is currently at after
exiting the loop:
What we need to do is bind the incrementor1
's value to the function when we
add it to the aFunctions
array. Using the pattern that we learned earlier, we
can create a closure around the incrementor1
variable by placing within an outer
function that returns the anonymous function. Calling it as an inline function
allows us to pass the incrementor1
variable to the inner function:
Now, when we execute the code, it behaves as we expect and displays 1, 2, and 3 in the alert boxes.
Many JS Frameworks make extensive use of closures within functions such as bind()
and curry()
to save the state of variables and object with functions and events
that will be using them at a later time - functions like setTimeout()
and setInterval()
,
as well as the XMLHttpRequest
's onreadystatechange
event handler. By using those,
you don't even have to know the nuts and bolts of closures to benefit from them.
Nonetheless, it behooves developers to spend the little bit of time
required to understand them.
Rob Gravelle combined his love of programming and music to become a software guru and accomplished guitar player. He created systems that are used by Canada Border Services, CSIS and other Intelligence-related organizations. As a software consultant, Rob has developed Web applications for many businesses and recently created a MooTools version of PHPFreechat for ViziMetrics. Musically, Rob recently embarked on a solo music career, after playing with Ivory Knight since 2000. That band was rated as one Canada's top bands by Brave Words magazine (issue #92) and released two CDs. Rob's latest, entitled KNIGHTFALL, was a collaboration between himself, the former Ivory Knight vocalist, and legendary guitarist/producer, Jeff Waters of Annihilator fame. His latest instrumental is available as a free MP3 download from his website. Rob is available for short-term software projects and recording session work. to inquire, but note that, due to the volume of emails received, he cannot respond to every email. Potential jobs and praise receive highest priority!
Original: July 6, 2009