Object-Oriented Programming with JavaScript

Object-Oriented-Programming-367x230

In this column we embark on a short series about Object-Oriented Programming (00P) with JavaScript. In Part I of this series, we will cover the fundamentals. We will show you how JavaScript fulfills one of the most important requirements from an object-oriented language: inheritance. The examples in this part will also demonstrate the other requirement: encapsulation. We’ll leave the third requirement, polymorphism, to other parts of this series.

Although JavaScript is a scripting language, its support of object-oriented programming is quite impressive. Even though there are no classes and instances, there are objects, prototypes, and implicit inheritance. We will explain in detail how to emulate inheritance and how the superclass-subclass relationship is formed. Prototyping is the key to understanding the inheritance concept. We’ll teach you how to establish prototyping, how to detect whether one object is a prototype of another object, and how JavaScript’s model is different from Java’s object-oriented programming. We’ll also show you how to check several attributes of the object’s properties.

The examples in this column are for JavaScript 1.1 and above, for Internet Explorer, unless noted otherwise.

Characterizing Object-Oriented Languages

Object-oriented design is based on three major principles: encapsulation, inheritance, and polymorphism. A programming language is said to support OO (Object-Oriented) design if it supports these three concepts in its syntax. Such a language should provide you with tools to easily define and use these paradigms. Encapsulation refers to the concept of making an object a “black box.” When you use an object, you should not know its internal workings. You don’t need to understand how an object works. An object should expose only the absolute necessary information needed to interface with it. It should give you a friendly interface to those limited set of methods and properties that the designer thought might be useful for other users. Encapsulation also means that an object includes everything it needs: both the data and the operations on it (methods). The encapsulation concept is very powerful because it allows an efficient division of labor in large software projects. Each team member can work on his or her object without interfacing too much with other members of the group. Overhead in development projects grows up exponentially with the number of interfaces between the group members. Encapsulation is a major contributor to OO design being a solution to the famous “Software Crisis.”

Software reuse is another characteristic of OO design. One of the major ways to achieve software reuse is by inheritance. A class is a function that defines an object. A superclass is a class from which new classes, subclasses, are created. A subclass inherits all its methods and properties from its superclass. Practically, all subclasses are generated automatically, and hence the enormous savings. You don’t have to define these subclasses one by one. Of course, you can override inherited methods and properties. In fact, there is no point to create a subclass which is a 100% duplication of its superclass, unless you override at least one property or one method.

Polymorphism is probably the most complicated component of the three must-haves. The essence of this concept is that every class should handle different data types. You shouldn’t create different classes to handle different data types. The classic example is the drawing class. You should not write different classes to draw circles, rectangles, and ovals. It should be one class that is smart enough to call the proper method to operate on the appropriate shape.

Inheritance through Functions

Although JavaScript does not support an explicit inheritance operator, you can implement inheritance in other ways. There are two different ways to establish a hierarchy of classes in JavaScript. The first method to create an object as a subclass of another object, is to call the superclass constructor function inside the subclass object definition. Let’s look at the following example:

function superClass() {

this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.inheritFrom = superClass;
this.inheritFrom();
this.bye = subBye;
}

function superHello() {
return “Hello from superClass”;
}

function superBye() {
return “Bye from superClass”;
}

function subBye() {
return “Bye from subClass”;
}
Click here to invoke the following assignment and function that activate these objects:

function printSub() {
var newClass = new subClass();
alert(newClass.bye());
alert(newClass.hello());
}
Convince yourself that it is working correctly. The methods bye() and hello() are first defined in superClass(). The method bye() is being overridden in subClass(). The first two lines of subClass() does the original inheritance between the two classes. You first define the inheritFrom method, and then you call it:

this.inheritFrom = superClass;
this.inheritFrom();
Let’s take another example. Here is the definition of superclass() and subclass() (different from the above superClass() and subClass()):

function superclass() {
this.info = alert(“Defining the Superclass”);
}

function subclass() {
this.inheritFrom = superclass;
this.inheritFrom();
this.info = alert(“Overriding the Superclass”);
}
To activate the generation of subclass(), click here. This button calls the following function:

function createSubclass() {
var newClass = new subclass();
}
Notice the alert boxes. They show that the info method is first defined in superclass() and then is being overridden in subclass().

Ineritance through Prototyping

The second and more robust method to establish a class hierarchy is by creating an object of the superclass and then assign it as a prototype of the subclass object. Suppose our superclass is superClass and our subclass is subClass. The prototype assignment would look like this:

subClass.prototype = new superClass;
Let’s take the example from Page 3 and use the prototype assignment instead of these assignments inside the subClass() defintion:
this.inheritFrom = superClass;
this.inheritFrom();
Here is the new code:

function superClass() {

this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
return “Hello from superClass”;
}

function superBye() {
return “Bye from superClass”;
}

function subBye() {
return “Bye from subClass”;
}
Click here to invoke the following script that creates an instance of subClass():

function printSub() {
var newClass = new subClass();
alert(newClass.bye());
alert(newClass.hello());
}
Convince yourself that you get the same results as in Page 3: bye() from subClass() and hello() from superClass().

Adding Properties after the Object is Created

Inheritance by prototyping (Page 4) is better than inheriting by function (Page 3) because it supports dynamic inheritance. You can define superclass methods and properties after the constructor is done, and have the subclass object pick the new methods and properties, automatically. Here is an example that defines the blessyou() function after the objects are created:

function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
return “Hello from superClass”;
}

function superBye() {
return “Bye from superClass”;
}

function subBye() {
return “Bye from subClass”;
}

var newClass = new subClass();

superClass.prototype.blessyou = superBlessyou;

function superBlessyou() {
return “Bless You from SuperClass”;
}

function printSub() {
alert(newClass.bye());
alert(newClass.hello());
alert(newClass.blessyou());
}
Notice the three lines:

superClass.prototype.blessyou = superBlessyou;

function superBlessyou() {
return “Bless You from superClass”;
}
We define the function blessyou() and then print it from the subclass object:

function printSub() {
var newClass = new subClass();
alert(newClass.bye());
alert(newClass.hello());
alert(newClass.blessyou());
}
Use the isPrototypeOf() method to find out if object2 had object1 in its prototype chain:

object1.prototype.isPrototypeOf(0bject2);
It returns true if object2 is an object and when object1 appears in the prototype chain of object2. Let’s look at an example:

function Person() {
this.name = “Rob Roberson”;
this.age = 31;
}

function Employee() {
this.dept = “HR”;
this.manager = “John Johnson”;
}

Employee.prototype = new Person();

var Ken = new Employee();
Ken is in the prototype chain of Employee, Person, and Object. Prove it to yourself by clicking on each class. They alert Employee.prototype.isPrototypeOf(Ken), Person.prototype.isPrototypeOf(Ken), and Object.prototype.isPrototypeOf(Ken), respectively.

Probing for Inheritance in NS

In Netscape Navigator 4.x and Netscape 6, JavaScript stores the prototyping relationship in an internal property of the object, __proto__ (two underscore characters at the front and two at the end.) Let’s take an example:

function Shape() {
this.borderWidth = 5;
}

function Square() {
this.edge = 12;
}

Square.prototype = new Shape;

myPicture = new Square;

alert(myPicture.__proto__);
alert(myPicture.borderWidth);
The object myPicture has an internal property, __proto__, which is set to Shape when the statement:

Square.prototype = new Shape;
is executed. The value of __proto__ instructs JavaScript where to look for properties, when local definitions are not available. In the example above, the property borderWidth is not defined within the object Square. The browser looks for the value of __proto__, which is Shape, and then fetches the value of its borderWidth property. Being an internal property, most browsers will return undefined when asked to alert __proto__. Netscape 6, though, returns [object Object]. Try it.

Let’s look at another example. In this example, UniversityAvenue inherits from Street, which inherits from City, which inherits from State:

function State() {
}

function City() {
}
City.prototype = new State;

function Street() {
}

Street.prototype = new City;

var UniversityAvenue = new Street();

function tryIt() {
alert(UniversityAvenue.__proto__== Street.prototype);
alert(UniversityAvenue.__proto__.__proto__== City.prototype);
alert(UniversityAvenue.__proto__.__proto__.__proto__== State.prototype);
alert(UniversityAvenue.__proto__.__proto__.__proto__.__proto__== Object.prototype);
alert(UniversityAvenue.__proto__.__proto__.__proto__.__proto__.__proto__== null);
}
All alert boxes will yield a value of true in Netscape Navigator 4.x and Netscape 6. Try it. __proto__ is not supported by Internet Explorer.

Emulating instanceOf()

Sometimes, you need to find out if a certain object is an instance of a given class (constructor() function). In other languages, this operation is called instanceOf(). JavaScript does not support the instanceOf() method, but we can write one ourselves, using the internal __proto__ (two underscores on each side) property. The algorithm is based on searching the object’s constructor along the inheritance chain, using __proto__:

function instanceOf(object, constructorFunction) {
while (object != null) {
if (object == constructorFunction.prototype) {return true}
object = object.__proto__;
}
return false;
}
The following code segment defines three classes: State, City, and Street. The Street’s prototype is City, and the City’s prototype is State. If UniversityAvenue is an instance of Street, it is also an instance of City and State. This demo (Netscape only) proves it by showing all the above instanceOf() relationships to be true:

function instanceOf(object, constructorFunction) {
while (object != null) {
if (object == constructorFunction.prototype) {return true}
object = object.__proto__;
}
return false;
}
function State() {
}

function City() {
}
City.prototype = new State;

function Street() {
}

Street.prototype = new City;

var UniversityAvenue = new Street();
function demo() {
alert(“instanceOf(UniversityAvenue, Street) is ” + instanceOf(UniversityAvenue, Street));
alert(“instanceOf(UniversityAvenue, City) is ” + instanceOf(UniversityAvenue, City));
alert(“instanceOf(UniversityAvenue, State) is ” + instanceOf(UniversityAvenue, State));
}
You can probe the superclass of any object via its constructor property. This property returns the function by which the object was created with the new operator. All objects (both native and user-defined) inherit from the Object object. Since the Object object supports the constructor property, all objects supports it as well. Let’s look at the Employee object:

function Employee() {
this.dept = “HR”;
this.manager = “John Johnson”;
}

function printProp() {
var Ken = new Employee();
alert(Ken.constructor);
}
When you ask for the constructor property, you should get a listing of the Employee function:

Classifying and Printing Objects

JavaScript supports three types of objects: native, host, and user-defined. Native objects are objects supplied by the JavaScript language. Object, Math, and Number are examples of native objects. Native object names start with a capital letter, and they are case-sensitive. To find the value of the mathematical constant PI, type Math.PI. If you try math.PI, you’ll get an error message. Host objects are provided by the browser for the purpose of interaction with the loaded document. Examples for host objects are: document, window, and frames. Host object names start with a lower-case letter. You can print on the screen the value of PI by document.write(Math.PI). Try Document.write() and you’ll get an error message. You define user-defined objects. You name the objects and you implement them. Your objects can start with either a lower-case letter or an upper-case letter, but they are case-sensitive. Here is an example that gives an error message because of case mismatch (try it):

function employee() {
this.dept = “HR”;
this.manager = “John Johnson”;
}

function printProp() {
var ken = new Employee();
for (property in ken) {
alert(property);
}
}
The Object object is the base object for all native and user-defined objects. All objects inherit from this superclass. You can create an Object object as well:

var myObject = new Object();
The Object object has a dozen of properties and methods. Examples are constructor, toString(), and valueOf(). The object myObject above sports these properties. Alert the object. You should get [object Object]. You can pass a string to the Object constructor:

var myObject = new Object(“foo”);
Alert the object. You should get the string you passed to the constructor, “foo”. The native objects sports the same properties, because they inherit from the Object class. Probe the constructor property of the Math object, Math.constructor. The answer you should get is that the Object object is the constructor of the Math object.

When you want to get a uniform feedback about an object’s names and content, the best way is to ask for their string version. Use the object’s methods of toString(), toLocaleString(), and valueOf(). When the object consists of a number, like:

var myNum = new Number(35);
the toString(), toLocaleString(), and valueOf() methods return the string “35”. When the object is user-defined, they return [object Object]. Try it for the following Employee object:

function Employee() {
this.dept = “HR”;
this.manager = “John Johnson”;
}

function printProp3() {
var Ken = new Employee();
alert(Ken.toString());
}
The toLocaleString() prints its object according to your definitions in the Control Panel’s Regional Settings file.

Querying an Object’s Properties

JavaScript provides you with a way to loop over an object’s properties. You can use this loop technique to find out the names of an object’s properties. The looping is done with a for loop:

for (property in objName)
where objName is the name of your object. In the following example we use the for loop to print ken’s employee properties (dept and manager):

function employee() {
this.dept = “HR”;
this.manager = “John Johnson”;
}

function printProp() {
var ken = new employee();
for (property in ken) {
alert(property);
}
}
Try it. You will get two alert boxes: one for dept and one for manager.

Use the hasOwnProperty() method to find out if an object has a certain property you are interested in. The hasOwnProperty() is a method of the Object object, and thus all objects inherit it (all objects are subclasses of the Object object). The following code checks if the object Ken of the Employee subclass has a manager. Try it (IE 5.x and up, Netscape 6 and up), it should give true:

function printProp2() {
var Ken = new Employee();
alert(Ken.hasOwnProperty(“manager”));
}
An object’s property may be enumerable. An enumerable property can assume only predefined values from a given list. The variable a is enumerable:

a = new Array(“jan”, “feb”, “march”);
It can assume one of three values only: “jan”, “feb”, or “march”. The following script defines the object Employee1, where the property month is enumerable:

function Employee1() {
this.dept = “HR”;
this.manager = “John Johnson”;
this.month = new Array(“jan”, “feb”, “mar”);
}

var Ken = new Employee1();
You can verify that indeed month is enumerable by:

Ken.month.propertyIsEnumerable(0);
The parameter of the method above should be numeric. Try it (IE 5.x and up, Netscape 6 and up).

Comparing JavaScript and Java

As opposed to Java, which is a class-based language, JavaScript is a prototype-based language. This difference is reflected everywhere. The term instance, for example, has a specific meaning in class-based languages. An instance is an individual member of a class. It is an implementation of the class definition. In JavaScript, the term “instance” does not have this technical meaning, because there is no such difference between classes and instances. However, instance can be used informally to mean an object created using a particular construction function. In the following example:

function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function subClass() {
this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
return “Hello from superClass”;
}

function superBye() {
return “Bye from superClass”;
}

function subBye() {
return “Bye from subClass”;
}

var newClass = new subClass();
newClass is an instance of subClass(). It was created according to subClass()’s constructor function.

How would the above example look in a class-based language? In Java, it is translated into something like that:

public class superClass {
public superClass () {
this.bye = superBye;
this.hello = superHello;
}
}
public class subClass extends superClass {
public subClass () {
this.bye = subBye;
}
}



Copyright 2017 JSResource.com. All rights reserved.

Posted March 19, 2017 by in category JavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *