Classes in JScript – Part III: Class Hierarchy and Data Encapsulation

In this post I will be discussing how one can achieve Class Hierarchy and Data Encapsulation in JScript.

I will take the same example that was used for the first and second parts and will keep updating it as and when required.

function Rectangle (ht, wt) {

                this.height = ht;

                this.width=wt;

}

Rectangle.prototype.area = function() {return this.height * this.width;}

var rect1 = new Rectangle(5,10);

var rect2 = new Rectangle(2,4);

var rect1Area = rect1.area(); // rect1.area() will return 50.

var rect2Area = rect2.area(); // rect2.area() will return 8.

Languages that support class based inheritance define two distinct entities: Classes and Instances. We defined Class in first part as the structure of an object. It defines exactly what fields an object contains, their types and methods to operate upon the object. A Class is an abstract thing. There can be several instances of a single Class. Each Instance has its own state.

However languages (e.g. JScript) that support prototype based inheritance do not make distinction between Class and Instances. These languages just have objects. A prototype-based language has the notion of a prototype object, which means that an object is used as a template from which one can get the initial properties for a new object. Also any object can be specified as a prototype for another object.

In Class based languages, a hierarchy of classes can be created through Class definitions. In the definition of a Class, we can specify that it is a derived class of an existing Class.

As described in part I, JScript does not support the keyword Class. It simulates Classes using Constructor and Prototype Objects. Hence implementing hierarchy of classes is achieved by using Constructor and Prototype Objects.

Let’s extend the above example – Rectangle to take co-ordinates. Ideally we would want to have all 8 co-ordinates topLeftX, topLeftY, topRight(X & Y),bottomLeft(X & Y) and bottomRight(X & Y), but for simplicity we will consider only the first two.

Earlier in this post I mentioned that “Also any object can be specified as a prototype for another object.” We use this to achieve inheritance by specifying super class object as a prototype for the child class.

Define the class PositionedRectangle:

function PositionedRectangle (topLeftX,topLeftY,height,width) {

                this.topLeftX = topLeftX;

                this.topLeftY = topLeftY;

}

Assign a new Rectangle object as the prototype for PositionedRectangle class.

PositionedRectangle.prototype = new Rectangle;

Create an object for the above type:

var prObj = new PositionedRectangle(2,3,4,5);

When prObj is created, it inherits the height and width properties from the Rectangle object due to the prototype set above.

On executing the below statement we can see the values assigned to topLeftX, topLeftY, height and width members.

alert(prObj.topLeftX+" "+ prObj.topLeftY+" "+ prObj.height+" "+ prObj.width); // displays 2 3 undefined undefined

 

Since the height and width properties of Rectangle do not get set, we see “undefined” in the output for them. This is a limitation of the above method. If we had a constructor function for Rectangle which would set default values for height and width properties, say

function Rectangle () {

                this.height = 1;

                this.width=1;

}

The output would be 2 3 1 1. It still wouldn’t take the values passed by us.

 

To overcome this, some code needs to be added to the constructor function for PositionedRectangle.

function PositionedRectangle (topLeftX,topLeftY,height,width) {

                this.topLeftX = topLeftX;

                this.topLeftY = topLeftY;

//One option would be:

this.height = height;

this.width=width;

//Or second option would be calling the constructor for Rectangle itself

Rectangle(height,width);

}

PositionedRectangle.prototype = new Rectangle;

var prObj = new PositionedRectangle(2,3,4,5);

alert(prObj.topLeftX+" "+ prObj.topLeftY+" "+ prObj.height+" "+ prObj.width);

The output now would be 2 3 4 5. Here we just invoke the super class constructor as a method of the child class with the correct set of arguments.

 

So how does this work?

1. First, the new operator creates a generic object and sets its prototype property to PositionedRectangle.prototype.

2. The new operator then passes the new object to the PositionedRectangle constructor as the value of the this keyword.

3. Next it initializes topLeftX and topLeftY.

4. Since Rectangle was called within the scope of the newly created object, its properties (height and width) are correctly set.

5. On returning from the PositionedRectangle constructor, JScript assigns the new object to prObj.

 

If we want to further create a subclass for PositionedRectangle class, then we need to set PositionedRectangle object as a prototype for the derived class and invoke PositionedRectangle’s constructor from the subclass constructor as a method of the subclass.

 

Data Encapsulation:

Data encapsulation means that an object's data members should remain private to the object and manipulated through publicly exposed methods. Simply saying programs should not have the ability to modify the properties of objects directly.

 

By default the members of an object are public. No methods are required to access or modify these members.

In the above code,

rect1.height = 10; // will change the height from its old value to 10

How can we make the members private?

The answer is nested functions and not using this keyword for members. Lets add a member colour to the Rectangle class. We want colour to be private and be modified using publicly exposed methods.

function Rectangle (ht, wt) {

                this.height = ht;

                this.width=wt;

                var colour = null; // colour now is a private member of this class.

                this.setColour = setColour;

                this.getColour=getColour;

//These methods will be used to access / modify colour.

                function setColour (passedColour) {

                                colour =passedColour;

                }

                function getColour() {

                                return colour;

}

}

var rect1 = new Rectangle(5,10);

rect1.setColour (“blue”); // this will set colour to blue.

alert(rect1.getColour); // this will display blue as output.

alert(rect1.colour) // this would display undefined.

Rect1.colour =”red”; /*this will not throw any error but wouldn’t update colour member too. It will create an expando with name colour on the object.*/

alert(rect1.getColour); // this will still display blue as output.

alert(rect1.colour) // this would displayred.

In this way we can define a private member and methods that manipulate it. Thus we can achieve Data Encapsulation in Jscript.

 

This is not everything that is there to talk about on Classes in Jscript, but this is good enough to get one started. Hope this post was helpful and fun to read!!

 

This completes the three parts that I had planned to write on this topic. Some more references to understand these concepts:

https://www.webdevelopersjournal.com/articles/jsintro3/js_begin3.html

https://dean.edwards.name/weblog/2006/03/base/

https://www.crockford.com/javascript/inheritance.html

https://devedge-temp.mozilla.org/viewsource/2001/oop-javascript/

Thanks,

Ritesh

SDET, JScript Team.