Understanding JavaScript Closures / Page 2 | WebReference

Understanding JavaScript Closures / Page 2


[prev]

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:

The trick here is that the 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


[prev]