Software Development

Really Understanding Javascript Closures

This post will explain in a simple way how Javascript Closures work. We will go over these topics and frequently asked questions:

  • What is a Javascript Closure
  • What is the reason behind the name ‘Closure’
  • Actually viewing closures in a debugger
  • how to reason about closures while coding
  • the most common pitfalls of it’s use

 
 

A Simple Example (bug included)

The simplest way to understand closures is by realizing what problem they are trying to solve. Let’s take a simple code example with a counter being incremented 3 times inside a loop.

But inside the loop, something asynchronous is done with the counter. It could be that a server call was made, in this case let’s simply call setTimeout that will defer it’s execution until a timeout occurs:

// define a function that increments a counter in a loop
function closureExample() {

    var i = 0;

    for (i = 0; i< 3 ;i++) {    
        setTimeout(function() {
            console.log('counter value is ' + i);
        }, 1000);
    }

}
// call the example function
closureExample();

Some things to bear in mind:

  • the variable i exists in the scope of the closureExample function and is not accessible externally
  • while looping through the variable the console.log statement is not immediately executed
  • console/log will be executed asynchronously 3 times, and only after each timeout of 1 second elapses
  • This means that 3 timeouts are set, and then the closureExample returns almost immediately

Which leads us to the main question about this code:

When the anonymous logging function gets executed, how can it have access to the variable ‘i’?

The question comes bearing in mind that:

  • the variable i was not passed as an argument
  • when the console.log statement gets executed, the closureExample function has long ended.

So What is a Closure then?

When the logging function is passed to the setTimeout method, the Javascript engine detects that for the function to be executed in the future, a reference will be needed to variable i.

To solve this, the engine keeps a link to this variable for later use, and stores that link in a special function scoped execution context.

Such a function with ‘memory’ about the environment where it was created is simply known as: a Closure.

Why the name Closure then?

This is because the function inspects it’s environment and closes over the variables that it needs to remember for future use. The references to the variables are closed in a special data structure that can only be accessed by the Javascript runtime itself.

Is there any way to See the Closure?

The simplest way is to use the Chrome Developer Tools debugger, and set a breakpoint in line 7 of the code snippet above.

When the first timeout gets hit, the closure will show up in the Scope Variables panel of the debugger:

WMRwmsi

As we can see, the closure is just a simple data structure with links to the variables that the function needs to ‘remember’, in this case the i variable.

But then, where is the Pitfall?

We could expect that the execution log would show:

counter value is 0
counter value is 1
counter value is 2

But the real execution log is actually:

counter value is 3
counter value is 3
counter value is 3

This is not a bug, it’s the way closures work. The logging function is a closure (or has a closure, as the term is used in both ways) containing a reference to the i variable.

This is a reference, and not a copy, so what happens is:

  • the loop finishes and the i variable value is 3
  • only later will the first timeout expire, and the logging function will log the value 3
  • the second timeout expires, and the logging function still logs 3, etc.

How to have a different counter value per async operation?

This can be done for example by creating a separate function to trigger the async operation. The following snippet would give the expected result:

function asyncOperation(counter) {  
    setTimeout(function() {
        console.log('counter value is ' + counter);
    }, 1000);
}

function otherClosureExample() {  
    var i = 0;

    for (i = 0; i < 3 ;i++) {    
        asyncOperation(i);
    }
}

otherClosureExample();

This works because when calling asyncOperation a copy is made of the counter value, and the logging will ‘close over’ that copied value. This means each invocation of the logging function will see a different variable with values 0, 1, 2.

Conclusion

Javascript closures are a powerful feature that is mostly transparent in the day to day use of the language.

They can be a convenient way to reduce the number of parameters passed to a function.

But mostly the fact that the closed variables are inaccessible to outside of the function makes closures a good way to achieve ‘private’ variables and encapsulation in Javascript.

Mostly the feature ‘just works’ and Javascript functions transparently remember any variables needed for future execution in a convenient way.

But beware of the pitfall: closures keep references and not copies (even of primitive values), so make sure that that is really the intended logic.

Reference: Really Understanding Javascript Closures from our JCG partner Aleksey Novik at the The JHades Blog blog.

Aleksey Novik

Software developer, Likes to learn new technologies, hang out on stackoverflow and blog on tips and tricks on Java/Javascript polyglot enterprise development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button