您的位置:首页 > 移动开发 > Objective-C

Object-Oriented Programming with JavaScript, Part I: Inheritance(RT)

2006-12-09 02:52 435 查看
adapted from http://www.webreference.com/js/column79/

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 both Internet Explorer and Netscape, unless noted otherwise.

In this column, you will learn:

How to characterize an object-oriented programming language
How to implement inheritance with functions
How to implement inheritance with prototyping
How to add properties after the object is created
How to probe inheritance in NS with __proto__
How to emulate instanceOf()
How to classify and print objects
How to query an object's properties
How to compare JavaScript and Java

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;
}
}

A Final Word

In this column we presented Part I of our series about object-oriented programming with JavaScript. We focused on the inheritance principle and how it is emulated by the language. We showed you two different ways to implement inheritance, and delved into the prototyping method. We demonstrated how to check for a superclass/subclass relationship between objects. We explained how to list an object's properties, and how to verify that indeed a certain property exists. We rounded up this column by a comparison of JavaScript and Java as object-oriented languages. In this column, you have learned: How to characterize an object-oriented programming language How to implement inheritance with functions How to implement inheritance with prototyping How to add properties and methods after the object is created How to probe inheritance in NS with __proto__ How to check for prototyping How to classify and print objects How to query an object's properties How to compare JavaScript and Java
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: