Class Member Encapsulation in JavaScript: Data Hiding

By Robert Gravelle

Encapsulation is the ability of an object to be a container (or capsule) for its member properties, including variables and methods. As a fundamental principle of object oriented programming (OOP), encapsulation plays a major role in languages such as C++ and Java. However, in scripting languages, where types and structure are not actively enforced by the compiler or interpreter, it is all-too-easy to fall into bad habits and write code that is brittle, difficult to maintain, and error-prone.

While JavaScript does not support Classes per se, it does allow for some of their main features, including data hiding, which is one of the main consequences of encapsulation. Several JS frameworks have objects that mimic classes in several ways, but if you don't want to add extra load time to your pages, it's easy enough to do it yourself.

An Explanation of Terms

A class is a template or prototype for each object instances made to the class design. The class specifies the properties (data) and methods (actions) that objects can work with.

Data hiding is the ability of objects to shield variables from external access. It is a useful consequence of the encapsulation principle. Those variables marked as private can only be seen or modified through the use of public accessor (getter) and mutator (setter) methods. This permits validity checking at run time. Access to other variables can be allowed but with tight control on how it is done. Methods can also be completely hidden from external use. Those that are made visible externally can only be called by using the object's getter and setter methods:

public class Person
{
  //properties/fields
  private int height = 68;
  private int weight = 170;
  private string name = "Rob Gravelle";
  private string socialInsuranceNumber = "555 555 555";

  //methods/actions
  public void setHeight(int height) {this.height=height;}
  public int getHeight() { return this.height; }
  public void setWeight(int weight) {this.weight = weight;}
  public int getWeight() { return this.weight; }
  public void setName(string name) {this.name=name;}
  public int getName() { return this.name; }
  public int setSocialInsuranceNumber(string socialInsuranceNumber) { this.socialInsuranceNumber=socialInsuranceNumber; }
}
//instanciate the Person class
Person aPerson = new Person();
string myName = aPerson.getName(); //myName now contains "Rob Gravelle"
aPerson.setName("mud"); //change the name
string myName = aPerson.getName(); //aPerson's name is now "mud"
string sinNo = aPerson.getSocialInsuranceNumber() //will throw an exception.  No getter implemented for that field!

Object Properties versus Variables

An object can store data in one of two ways: as a property or as a variable. The method that you choose will have substantial ramifications on data visibility. Object properties are created using the "this." prefix. The name that follows the dot will identify a new property to add to the object's properties collection. That makes it a poor choice for storing private data. Variables, on the other hand, are created using the "var" keyword. It is subject to the rules of variable scoping and are more private for that reason. The following Person object contains a number of properties, accessible via setter and getter functions:

function Person()
{
  //properties/fields
  this.name = "Rob Gravelle";
  this.height = 68;
  this.weight = 170;
  this.socialInsuranceNumber = "555 555 555";

  //methods/actions
  this.setHeight = function(height) {this.height=height;}
  this.getHeight = function() { return this.height; }
  this.setWeight = function(weight) {this.weight = weight;}
  this.getWeight = function() { return this.weight; }
  this.setName   = function(name) {this.name=name;}
  this.getName   = function() { return this.name; }
  this.setSocialInsuranceNumber = function(socialInsuranceNumber) { this.socialInsuranceNumber=socialInsuranceNumber; }

  return this;
}
//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.getName(); //myName now contains "Rob Gravelle"
aPerson.setName("mud"); //change the name
var myName = aPerson.getName(); //aPerson's name is now "mud"
var sinNo = aPerson.getSocialInsuranceNumber() //will also throw an exception.  No getter implemented for that field!

The above code may look like a reasonable substitute for the Java Class at a glance, but the data hiding that is inherent to the class is not the case for JavaScript objects. In fact, an easy way to prove it is to try calling the fields directly:

  //instanciate the Person class
  var aPerson = new Person();
  var myName = aPerson.name;  //this works
  //as does this:
  aPerson.name = "whatever."

The lesson here is that object member variables are public, while those that are stored as variables are private. Our second example uses variables instead of properties to hide all the data fields:

function Person()
{
  //properties/fields
  var name = "Rob Gravelle";
  var height = 68;
  var weight = 170;
  var socialInsuranceNumber = "555 555 555";

  //methods/actions
  this.setHeight = function(newHeight) {height=newHeight;}
  this.getHeight = function() { return height; }
  this.setWeight = function(newWeight) {weight = newWeight;}
  this.getWeight = function() { return weight; }
  this.setName   = function(newName) {name=newName;}
  this.getName   = function() { return name; }
  this.setSocialInsuranceNumber  = function(newSocialInsuranceNumber) { socialInsuranceNumber=newSocialInsuranceNumber; }

  return this;
}
//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.name;  //this no longer works
var myName = aPerson.getName();  //this does

Notice that the setters' argument is named differently than the underlying variable. That's to avoid duplicate variables within the same scope.

Using Object Notation

In JavaScript, Object Literals are created using a slightly less strict version of JavaScript Object Notation (JSON). Less strict in that the identifiers don't need to be enclosed inside quotation marks. Curly brackets ({}) identify an object literal. Each class member is comprised of an identifier, followed by a colon (:) and the property's value. Class members are separated by a comma (,). We still need the Person() function in order to separate the private members from the accessor and mutator methods and so that we can instanciate new objects of the same type:

function Person() {
  //properties/fields
  var name = "Rob Gravelle";
  var height = 68;
  var weight = 170;
  var socialInsuranceNumber = "555 555 555";

  return {
    setHeight: function(newHeight) {height=newHeight;},
    getHeight: function() { return height; },
    setWeight: function(newWeight) {weight = newWeight;},
    getWeight: function() { return weight; },
    setName:   function(newName) {name=newName;},
    getName:   function() { return name; },
    setSocialInsuranceNumber: function(newSocialInsuranceNumber) { socialInsuranceNumber=newSocialInsuranceNumber; }
  };
}

//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.getName();
alert(myName); //prints "Rob Gravelle"

Conclusion

JavaScript 2.0 is supposed to be more object-oriented, but it's been an awfully long time coming. While you're waiting, These techniques can help you add a more traditional OOP structure to your JavaScript code. The next article will deal with the slightly more challenging subject of method hiding.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •