jmnf 8: stricter encapsulation

javascript magic-ninja-foo episode 8: Objects part 5 stricter encapsulation.

This is the second part in a discussion on encapsulation in javascript. The previous article should have been mostly obvious to the regular reader, or those intimately familiar with object oriented concepts but I felt it was necessary to provide a simple foundation for encapsulation.

There is an alternate way to enforce stricter encapsulation, although this approach incurs tradeoffs. Here again the articles converge. Remembering functions with state, and understanding scope, we can begin to get some ideas on how this might be accomplished.

To illustrate this I'm going to redefine the constructor function from the previous article in a slightly different way. Again, if you're a regular reader, you might already see the implications of javascript written in this way.

(protip: paste the examples into your broser console to try them out)

/* Constructor function */
function Orange() {
   var color = "orange";
   var type = "citrus";
   /*color setter - lexical closure*/
   function setColor(incolor) {
      color = incolor;
   }
   /*color getter - lexical closure*/
   function getColor() {
      return color;
   }
   /*assign the inner functions as
     methods to this the new
     object*/
   this.setColor = setColor;
   this.getColor = getColor;
}
/*make an orange object*/
var myOrange = new Orange();
/*set then get, blue oranges?*/
myOrange.setColor("blue");
alert(myOrange.getColor());

The first thing to note is that we've defined a few variables using the var keyword inside the constructor function. I've said before that variables and properties of objects are fundamentally the same thing. These 'inner' variables become properties of the 'execution object' that is created during the execution of the constructor function.

Normally, once the constructor function has completed, the execution object is garbage collected. However, since we've assigned the inner lexically scoped functions (episode 3) as methods on the new object, we've essentially tied the newly created object to the execution object keeping the execution object from being garbage collected. This technique is similar to 'functions with state' (episode 3) except that we can 'return' as many methods as we wish pointing into the execution object rather than a single return function. This is accomplished using the temporary binding of the new and 'execution object' that occurs during object creation through the 'this' keyword (episode 4).

There is no direct way to access the 'execution object' therefore there is no easy way to circumvent the getters and setters to access the two "var" defined variables. This effectively makes them strictly private. While the new object exists we continue to hold onto the 'execution object' to act as a private container to hold state variables. This effectively provides the illusion of a single object with potentially private instance variables and methods.

It might be easier to define what's going on graphically. We'll start by talking about what happens in the example from the previous article and then how this approach differs. Hopefully this will help in understanding what's going on but also begin to show what the tradeoffs are for using this approach..

The first case:

The key here is that the constructor function 'execution object' does it's job, sets up the properties of the new object, and then is GC'd after it's work is done.

The second case:

In this case, however, by having external references to the 'execution object' it remains in existence. Since the 'getter and setters' were defined inside the constructor function, they have access to the 'outer' lexical scope of that function (the var variables).

There are two prices to pay for this approach the first is flexibility. In the previous article, when I decided to add boundary checks to the setter method I intentionally dynamically altered the setter method during runtime. Taking advantage of the fluid nature of objects, I was able to redefine the setter method without stopping and restarting the application.

With this 'strict' approach, once the constructor function completes there is no longer any reference to its 'execution object'. This makes it impossible to alter any method operating directly on the 'execution object's' properties, effectively keeping them private. You could still create a 'wrapper' method for boundary checks, but the actual method defined inside the 'execution object' that has access to the 'private' variables can no longer be altered. This is either a positive or a negative depending on how you write javascript code.

The second, more important, price to pay for this is memory. The case described in the previous article could be argued to be very efficient. Function objects exist only while executing and then are garbage collected. In the latter case, for every object you create that has strict 'private' variables, you're creating two objects, one to hold your private state variables and methods (by hanging onto the 'execution object'), and another to hold your public state variables and methods. The public methods act as the 'gateway' to access those methods and variables within the 'private' object.

It's also possible to mix and match these approaches. Objects that need 'private' methods and variables could be designed with the latter approach, and objects with less stringent requirements could be made with the former approach.

Javascript is extremely flexible, so there are likely other ways to accomplish this same end effect. As always, your mileage may vary.

Next Episode