Section 5.4. Nested Functions, Function Closure, and Memory Leaks


5.4. Nested Functions, Function Closure, and Memory Leaks

Again, this is fairly advanced JavaScript programming, but because it occurs quite frequently in Ajax programming, I felt it best to include in the book. However, as with recursion, you may want to finish the book and then return to this section.


Another interesting aspect of function literals in JavaScript is their use as nested functions. Consider the following:

function outer (args) {    function inner (args) {       inner statements;    } }

With a nested function, the inner function operates within the scope of the outer function, including having access to the outer function's variables and arguments. The outer function, though, does not have access to the inner function's variables, nor does the calling application have access to the inner function. (Well, not unless it's created as a function literal and returned to the calling application, which then adds its own complication.)

Example 5-6 demonstrates creating a nested, inner-function literal, which is then returned to the calling application. The inner function uses the outer function's one argument, as well as its one variable. When the inner function is returned to the calling application and invoked directly, it concatenates the string passed as a parameter to the original outer-function call to the string passed to it directly as an argument. The inner function concatenates this string with that created as the local variable in the outer function, and then returns the result. Changing the argument to the inner function changes the string, as does calling the outer function again, to get another instance of the inner function.

Example 5-6. Nested functions and closure

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Getting Closure</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <script type="text/javascript"> //<![CDATA[ // outer function function outerFunc(base) {    var punc = "!";    // inner function    function returnString(ext) {      return base + ext + punc;    }        return returnString; } // create access to inner function var baseString = outerFunc("Hello "); // inner function still has access to outer function argument var newString = baseString("World"); document.writeln(newString); // and still var notherString = baseString("Reader"); document.writeln(notherString); // create another instance of inner function var anotherBase = outerFunc("Hiya, Hey "); // another local string var lastString = anotherBase("you"); document.writeln(lastString); //]]> </script> </body> </html>

The result is this line in the web page:

Hello World! Hello Reader! Hiya, Hey you!

Pretty nifty stuff. The only question is, how does this work? Isn't this in violation of scoping rules, which state that when a function terminates, all of the memory for its local variables gets released via automatic garbage collection?

Not quite.

Each time a new scope is created in a JavaScript application, an associated scoping bubble, if you will, is created to enclose it. This applies to functions, which operate in their own scope.

Normally, when the function terminates, the scope is released because it's no longer necessary. However, in the case of an inner function that's returned to the outer application and assigned to an external variable, the scope of the inner function is attached to the outer, which is in turn attached to the calling applicationjust enough to maintain the integrity of the function literal and the outer-function argument and variable. Returning a function literal created as an internal object within another function, and assigning it to a variable in the calling application, is known as closure in JavaScript. And it is the scope chaining that ensures that the data necessary for this to work is in place.

This is very neat stuff, and you can see intriguing uses of closure in creating new objects or extending existing ones. We'll explore these further later in the book; however, there's another problem associated with closure.

Closures can be created accidentally or used unintentionally. In Example 5-6, if a new reference to the inner function is created for each string created, rather than reusing the variable referencing the inner function, there will be a lot of instances of that object over time.

Accidental closure can also occur when a circular reference is created, such as the following from the Mozilla documentation site:

function leakMemory(  ) {     var el = document.getElementById('el');     var o = { 'el': el };     el.o = o; } 

We'll get into the Document Object Model in Chapter 10, but in this case, the DOM is accessed to get an element identified by el. This is used to create a new object reference using a very abbreviated form of a function literal. That object creates an unnamed object that assigns the retrieved DOM object to a property identified by a property name of el.

Then comes the kicker: we assign this to the variable referencing the original object, which literally means we've assigned the object as a property of itself. This is not something I want to encourage, but most browsers can manage to terminate the closure and reclaim the memoryexcept Internet Explorer.

IE provides its own memory management for DOM objects, in addition to memory management for JavaScript objects. In the case of accidental closures caused from such circular references as this and the crossover between JS and DOM objects, the memory is allocated and never freednot even when the page is closed. In fact, the only time the memory is freed is when the browser is closed.

The memory leak that results is usually small, unless you put all of this into a loop, in which case the memory loss could quickly build. This explains why you should use the power of closure with caution.

For an excellent overview of closures, see the paper by Jim Ley on the topic at http://jibbering.com/faq/faq_notes/closures.html.





Learning JavaScript
Learning JavaScript, 2nd Edition
ISBN: 0596521871
EAN: 2147483647
Year: 2006
Pages: 151

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net