brown mouse wearing black glasses with front feet on keyboard

JavaScript Closures

Closures are crucial for effectively leveraging the JavaScript language; however, too many programmers don't know about or understand this important concept. A common situation that utilizes closures is when you have a function definition that's nested within a parent function that returns the nested function object as the value of the parent function. In this situation, the nested function retains references to the local variables in the parent function, even after the parent function finishes executing.

Another way to describe closures is that nested functions have access to the local variables of the functions they are defined within and retain references to those variables even when the inner function has a longer lifetime than the parent function. (I credit Douglas Crockford, Yahoo!’s chief JavaScript architect, for inspiring this description.)

I could write endlessly about what closures do, but the following code shows an example that’s far more effective in demonstrating the concept. Consider the following function definition, functionalScoping, which defines a local variable and sets its value to a string. The function defines a nested function that displays the value in the message variable to the console, invokes the nested function, and returns. The result is that the displayMsg function writes "JavaScript closures rock!" to the console. A nested function has access to its parent's variables in scope, so displayMsg has access to the message variable. This example presents nothing new or surprising, and it doesn’t use closure in any meaningful way. This method is called functional scoping, as the parent function name implies.

function functionalScoping() {
   var message = "JavaScript closures rock!";

   function displayMsg() {
      console.log(message);
   }
   displayMsg();
}

functionalScoping();

The next example shows a variation of the functional scoping code. The createFunction definition creates a message variable and a displayMsg nested function that writes the message value to the console. Instead of invoking displayMsg within the createFunction function, this new function returns the displayMsg function object to the calling code. When code invokes createFunction, the calling code gets a reference to the displayMsg function, but doesn't invoke displayMsg.

function createFunction() {
   var message = "JavaScript closures rock!";

   function displayMsg() {
      console.log(message);
   }
   return displayMsg;
} 

The following code invokes createFunction and saves the reference to the returned function to the newFunc variable, then invokes the function returned by createFunction:

var newFunc = createFunction();
newFunc();

Here's where things get interesting: The result of executing the previous code is that the message, “JavaScript closures rock!” is written again to the console. How can this be? Isn't the message variable out of scope when the function in newFunc is invoked? If that's the case, shouldn't the undefined message be written to the console as undefined?

Not at all! This example shows closure at work. When createFunction defined the displayMsg function, the nested function closed around the variables in scope, retaining a reference to the message variable as long as the nested function is in scope. When the displayMsg function was invoked through the newFunc variable, the message was still in scope and retained its original string value. That’s the power of closures.

It’s important to understand that the nested function retains a reference to the variables, not just the value. The final example shows code that defines a variation of the createFunction function. The difference here is that after createFunctionA defines the displayMsg function, it changes the value of the message variable. Now, “We have great JavaScript courses!” is written to the console pane instead of the original message.

function createFunctionA() {
   var message = "JavaScript closures rock!";

   function displayMsg() {
      console.log(message);
   }
   message = "We have great JavaScript courses!";
   return displayMsg;
}

newFunc = createFunctionA();
newFunc();

That's the basics of closure, which has benefits far beyond these simple examples. Next time, I'll dig into closures more deeply and explore how closures depend on the scope chains associated with JavaScript functions. I derived this material from a JavaScript course I developed for AppDev.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish