JavaScript Closure Misunderstanding

Let’s list some well-known facts:

  • JavaScript blocks don’t have scope.
  • Only functions have scope.
  • Variable access speed depends on “distance” of scopes (in which scope is defined a variable and from which scope we want to retrieve it).
  • The furthest scope is global scope and it is considered as slowest scope (That is window object in browser implementation of JavaScript).
  • Closures are used to cache scope locally and make variable access faster (and for much more…).

Classical closure example is:

//without closure
window.singeltonObject = {/* properties of singleton object */};
getSingeltonObject = function(){
   return singeltonObject;
};
//with closure
getSingeltonObject = (function(){
    var singeltonObject = {/* properties of singleton object */};
    return function(){
          return singeltonObject;
    }
})();

Closure version looks difficult but it is very powerful (for example you can not modify your singelton object, it is closed in a local scope, while when you have defined it in global scope it is accessible from everywere). Les’t explain it:

//without closure
//global scope
window.singeltonObject = {};
getSingeltonObject = function(){
    //local scope
   return singeltonObject; //goes out of local scope in
                            //global scope, finds variable and
                            //reads it.
}
//with closure
//global scope
getSingeltonObject = (function(){
    //local scope1
    var singeltonObject = {};
    return function(){
          //local scope2
          //while definition of this function local scope1
          //is cached and singeltonObject will be found there,
          //it will not be searched in global scope
          //that's why it is faster and useful.
          return singeltonObject;
    }
})();

What happens when we have three or multiple deep closure? I mean, when we have such kind of code:

/**
*  @param {Number} personId
*  @return {Object}
*     Has method logPersonId that logs parameter
*/
getSingeltonObject = (function(){
    //scope0
    var singeltonObject;
    return function(personId){
          //scope1
          if(!singeltonObject){
               singeltonObject = {
                    logPersonId : function(){
                        console.log(personId);
                    }
               }
          }
          return singeltonObject;
    }
})();

At first glance everything is OK, but there is a bug:

getSingeltonObject(1).logPersonId(); // 1 will be logged
getSingeltonObject(123).logPersonId(); //still 1 will be
                              //logged instead of 123 :) why?

Because the function logPersonId is defined once, it caches the scope1 and at that time the parameter personId equals to 1. During the second call logPersonId is already defined and it has cached scope1 when personId was equal to 1 and it still “remembers” it. That is why in both calls value 1 was logged.

What is solution? scope0. We have to update personId in scope0 by creating a new variable in it (for example newPersonId) and assigning it personId everytime the getSingeltonObject is called. The code will look like this:

getSingeltonObject = (function(){
    //scope0
    var singeltonObject, newPersonId;
    return function(personId){
         //scope1
         newPersonId = personId;
          if(!singeltonObject){
               singeltonObject = {
                    logPersonId : function(){
                        console.log(newPersonId); //newPersonId
                    //will be found in scope0 and it will
                    //equal to parameter of getSingeltonObject
                    //function every time.
                    }
               }
          }
          return singeltonObject;
    }
})();

This was a little about JavaScript closure and its complexity.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: