Thursday, January 3, 2008

Javascript Namespaces : Privacy thru closure or how to type less!

we have seen in the last entry how we could create a basic namespace simulation in javascript. We have seen how to import in the global space what we needed. Packages should not though import anything to keep the global space clean but it could be nice to shortcut things within packages too. We will see how to do so thanks to closures.

Privacy thru functions


If you read the entry Javascript scoping: things to keep in mind, you certainly remember that it is possible to create scoping thru functions, therefore, the following variables won't be available outside of the scope of the function:
function privacy() {
    var myvar=1; // can't be accessed from outside
    myvar    =2: // can be accessed from outside
}
We can create a function by putting a reference to it in a variable too :
privacy = function() {
    var myvar=1; // can't be accessed from outside
    myvar2    =2: // can be accessed from outside 
}
alert(myvar); //error
alert(myvar2); //error
privacy();
alert(myvar); //error
alert(myvar2); //2
We are using an anonymous function which definition is referenced to the privacy variable that will act like a normal function now.
But as you can see we need to call the privacy function in order to access the public variable we have created. It won't be available as long as we do not execute the function.

Closures as scope container


There is a way to create scoping without even having to name the function at all. we just need to define the anonymous function and have it execute right away.
we can do this, thanks to the following notation:
(function () {

 //your code here


})();
This is just an anonymous function held within a block. By doing so, the code defined in the anonymous function will be executed right away and therefore, what is defined within it will be available to us.
Let's try:
(function() {
   alert('hi!');
})();
If you try this code, you get the alert prompt saying hi!.
In fact within this closure, you can write your javascript code like you always do!!
It will create a space hold in memory but not accessible from outside.
Well unless, you allow variables to be accessed from outside:
(function() {
    var myvar=1; // can't be accessed from outside
    myvar    =2: // can be accessed from outside 
})();
Closures are a very powerfull tool allowing many many other things!

Packages and closure : creating privacy for us, the lazy!


we have seen different packages holding some utilities function developped so far. There is especially one of them that has a dependency to an other package, the number package. Let's see the package in question:
JAME.Package('JAME.Util.Color');

JAME.Util.Color = {
    rgb2h:function (r,g,b) { 
       var d2h = JAME.Util.Number.d2h;
       return [d2h(r),d2h(g),d2h(b)];
    },
    h2rgb:function (h,e,x) {  
       var h2d = JAME.Util.Number.h2d;
       return [h2d(h),h2d(e),h2d(x)];
   },
   cssColor2rgb:function (color) {
       if(color.indexOf('rgb')<=-1) {
  return this.hexStr2rgbArray(color);
       }
       return this.rgbStr2rgbArray(color);
  },
  hexStr2rgbArray:function (color) {
      return this.h2rgb(color.substring(1,3),
                        color.substring(3,5),
                        color.substring(5,7));
  },
  rgbStr2rgbArray:function (color) {
     return color.substring(4,color.length-1).split(',');
  },
  Export : function() {
     JAME.Exporter(this);
  }
};
You can see that we have created a shortcut for 2 functions.
We have placed them within the function definition in order not to put them into the global space.
it was fine, because we only need them in one place but if we had them used in different places? Create a property into the object definition to hold them could be a solution but will polluate the package a little.
In that case, it is easier to create a closure to hold all the dependencies we require, therefore we can rewrite it this way:
JAME.Package('JAME.Util.Color');

  
(function () {

      var d2h = JAME.Util.Number.d2h;
      var h2d = JAME.Util.Number.h2d;

      JAME.Util.Color = {
           rgb2h:function (r,g,b) { 
              return [d2h(r),d2h(g),d2h(b)];
           },
           h2rgb:function (h,e,x) {  
              return [h2d(h),h2d(e),h2d(x)];
           },
           cssColor2rgb:function (color) {
           if(color.indexOf('rgb')<=-1) {
          return this.hexStr2rgbArray(color);
           }
           return this.rgbStr2rgbArray(color);
           },
           hexStr2rgbArray:function (color) {
           return this.h2rgb(color.substring(1,3),
                                    color.substring(3,5),
                                    color.substring(5,7));
           },
           rgbStr2rgbArray:function (color) {
           return color.substring(4,color.length-1).split(',');
           },
           Export : function() {
                JAME.Exporter(this);
           }
      };

})();
the 2 shortcuts won't be available outside and therefore will keep the global space clean while allowing less typo!

Conclusion


You should be aware that a closure will keep everything defined in it and that it will stay in memory as long as the program goes. It can also lead to memory leaks especially in IE when mixed with DOM manipulation.
It is therefore a very powerfull tool but can have underisable effects that won't be noticable right away.

1 comment:

shiriru said...

Hi,

thanks for reading.

I am not really sure of what you want to do but the following code just work fine here:


var com ={}
com.util={};
com.util.greeting = function(str) {
return 'hello '+str;
};

(function() {

var greeting = com.util.greeting;

MyObj = function() {

};

MyObj.prototype.someMethod = function() {
alert(greeting('world!'));
};

}) ();

var testing = new MyObj();
testing.someMethod();


Is it what you're trying to do?