JavaScript Tutorial/Object Oriented/Inheritance

Материал из Web эксперт
Перейти к: навигация, поиск

Adding new properties and methods to the child class

All new properties and methods must be added after the line that deletes the new method.



   <source lang="javascript">

function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

}

function SubClass(sColor, sName) {

   this.newMethod = BaseClass;
   this.newMethod(sColor);
   delete this.newMethod;
   this.name = sName;
   this.sayName = function () {
       alert(this.name);
   };

} var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName();</source>


All new properties and methods of the subclass must come after the assignment of the prototype property

   <source lang="javascript">

function SubClass() { } SubClass.prototype = new BaseClass(); SubClass.prototype.name = ""; SubClass.prototype.sayName = function () {

   alert(this.name);

}; var objA = new BaseClass(); var objB = new SubClass(); objA.color = "red"; objB.color = "blue"; objB.name = "MyName"; objA.sayColor(); objB.sayColor(); objB.sayName();</source>


Class Inheritance

   <source lang="javascript">

<html> <head> <title>Inheritance</title> <script type="text/javascript"> function Song(title,type) {

  this.title = title;
  this.type = type;
  this.getTitle=function() {
     return "Song: " + this.title + " Type: " + this.type;
  }

} function SubSong(title,type,artist) {

  this.artist = artist;
  this.toString("Artist is " + artist);
  Song.apply(this,arguments);
  this.toString = function () {
    return "Artist: " + this.artist + " " + this.getTitle();
  }

} SubSong.prototype = new Song(); var song = new SubSong("name", "type", "Artist"); alert(song.toString()); </script> </head> <body> </body> </html></source>


Hybrid Method for class inheritance

In the SubClass constructor, object masquerading is used to inherit the color property from BaseClass.

Then prototype chaining is used to inherit the methods of BaseClass.



   <source lang="javascript">

function BaseClass(sColor) {

   this.color = sColor;

} BaseClass.prototype.sayColor = function () {

   alert(this.color);

}; function SubClass(sColor, sName) {

   BaseClass.call(this, sColor);
   this.name = sName;

} SubClass.prototype = new BaseClass(); SubClass.prototype.sayName = function () {

   alert(this.name);

}; var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName();</source>


Inheritance

Call the constructor from base class

A constructor assigns all properties and methods using the this keyword.

You can make the constructor of BaseClass into a method of SubClass and call it.

After the calling, subClass receives the properties and methods defined in BaseClass"s constructor.

In the following code, the "newMethod" is assigned to BaseClass.

Then, the "newMethod" is called, passing the color argument from the SubClass constructor.

The final line of code deletes the reference to BaseClass so that it cannot be called later on.



   <source lang="javascript">

function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

} function SubClass(sColor) {

   this.newMethod = BaseClass;
   this.newMethod(sColor);
   delete this.newMethod;

} var objA = new BaseClass("red"); var objB = new SubClass("blue"); objA.sayColor(); objB.sayColor(); objB.sayName();</source>


Prototype Chaining

The downside to prototype chaining is that it has no support for multiple inheritance.

Prototype chaining involves overwriting the prototype property of the class with another type of object.

You can set the prototype property of SubClass to be an instance of BaseClass to inherit all the properties and methods of BaseClass.



   <source lang="javascript">

function BaseClass() { } BaseClass.prototype.color = "red"; BaseClass.prototype.sayColor = function () {

   alert(this.color);

}; function SubClass() { } SubClass.prototype = new BaseClass();</source>


The Apply() Method

The apply() method takes two arguments.

The first one is the object to be used for this.

The second one is an array of arguments to be passed to the function.



   <source lang="javascript">

function sayColor(sPrefix, sSuffix) {

   alert(sPrefix + this.color + sSuffix);

}; var obj = new Object(); obj.color = "red"; sayColor.apply(obj, new Array("The color is ",", a very nice color indeed."));</source>


The Call() Method

The first argument of the Call() Method is the object to be used for "this".

All other arguments are passed directly to the function itself.



   <source lang="javascript">

function sayColor(sPrefix, sSuffix) {

   alert(sPrefix + this.color + sSuffix);

}; var obj = new Object(); obj.color = "red"; sayColor.call(obj, "The color is ", ", a very nice color indeed. ");</source>


Three-level inheritance

   <source lang="javascript">

<html> <head> <title>Example</title> </head> <body> <script type="text/javascript"> function Shape(iSides) {

   this.sides  = iSides;
   if (typeof Shape._initialized == "undefined") {
       Shape.prototype.getArea = function () {
           return 0;
       };
       Shape._initialized = true;
   }

} function Triangle(iBase, iHeight) {

   Shape.call(this, 3);
   this.base = iBase;
   this.height = iHeight;
   if (typeof Triangle._initialized == "undefined") {
       Triangle.prototype.getArea = function () {
           return 0.5 * this.base * this.height;
       };
       Triangle._initialized = true;
   }

} Triangle.prototype = new Shape(); function Rectangle(iLength, iWidth) {

   Shape.call(this, 4);
   this.length = iLength;
   this.width = iWidth;
   if (typeof Rectangle._initialized == "undefined") {
       Rectangle.prototype.getArea = function () {
           return this.length * this.width;
       };
       Rectangle._initialized = true;
   }

} Rectangle.prototype = new Shape();

var triangle = new Triangle(12, 4); var rectangle = new Rectangle(22, 10); document.write("triangle.sides:"+triangle.sides); document.write("
"); document.write("triangle.getArea():"+triangle.getArea()); document.write("
"); document.write("rectangle.sides:"+rectangle.sides); document.write("
"); document.write("rectangle.getArea():"+rectangle.getArea()); </script> </body> </html></source>


Three level inheritance by using the Hybrid Method

   <source lang="javascript">

function Shape(iSides) {

   this.sides = iSides;

} Shape.prototype.getArea = function (ss) {

   return 0;

}; function Triangle(iBase, iHeight) {

   Shape.call(this, 3);
   this.base = iBase;
   this.height = iHeight;

} Triangle.prototype = new Shape(); Triangle.prototype.getArea = function () {

   return 0.5 * this.base * this.height;

}; function Rectangle(iLength, iWidth) {

   Shape.call(this, 4);
   this.length = iLength;
   this.width = iWidth;

} Rectangle.prototype = new Shape(); Rectangle.prototype.getArea = function () {

   return this.length * this.width;

}; var triangle = new Triangle(12, 4); var rectangle = new Rectangle(22, 10); alert(triangle.sides); //outputs "3" alert(triangle.getArea()); //outputs "24" alert(rectangle.sides); //outputs "4" alert(rectangle.getArea()); //outputs "220"</source>


Using apply function to call base constructor

   <source lang="javascript">

<html> <head> <title>Example</title> </head> <body> <script type="text/javascript"> function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

} function SubClass(sColor, sName) {

   BaseClass.apply(this, arguments);
   this.name = sName;
   this.sayName = function () {
       alert(this.name);
   };

} var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName(); </script> </body> </html></source>


Using "apply method" to call the constructor of the base class

   <source lang="javascript">

function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

} function SubClass(sColor, sName) {

   BaseClass.apply(this, new Array(sColor));
   this.name = sName;
   this.sayName = function () {
       alert(this.name);
   };

} var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName();</source>


Using call function to call the constructor from base class

   <source lang="javascript">

<html> <head> <title>Example</title> </head> <body> <script type="text/javascript"> function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

} function SubClass(sColor, sName) {

   BaseClass.call(this, sColor);
   this.name = sName;
   this.sayName = function () {
       alert(this.name);
   };

} var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName(); </script> </body> </html></source>


Using "call method" to call the constructor of base class

   <source lang="javascript">

function BaseClass(sColor) {

   this.color = sColor;
   this.sayColor = function () {
       alert(this.color);
   };

} function SubClass(sColor, sName) {

   BaseClass.call(this, sColor);
   this.name = sName;
   this.sayName = function () {
       alert(this.name);
   };

} var objA = new BaseClass("red"); var objB = new SubClass("blue", "MyName"); objA.sayColor(); objB.sayColor(); objB.sayName();</source>


Using xbObjects to build three-level inheritance

   <source lang="javascript">

<html> <head> <title>Example</title> <script type="text/javascript"> /*

* xbObjects.js
* $Revision: 1.2 $ $Date: 2003/02/07 16:04:20 $
*/

/* ***** BEGIN LICENSE BLOCK *****

* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Bob Clary code.
*
* The Initial Developer of the Original Code is
* Bob Clary.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Bob Clary <bc@bclary.ru>
*
* ***** END LICENSE BLOCK ***** */

/* ChangeLog: 2001-12-19 - bclary - changed xbException init method to

          remove possible exception due to permission denied issues
          in gecko 0.9.5+
  • /

function _Classes() {

 if (typeof(_classes) != "undefined")
   throw("Only one instance of _Classes() can be created");
 function registerClass(className, parentClassName)
 {
   if (!className)
     throw("xbObjects.js:_Classes::registerClass: className missing");
   if (className in _classes)
     return;
   if (className != "xbObject" && !parentClassName)
     parentClassName = "xbObject";
   if (!parentClassName)
     parentClassName = null;
   else if ( !(parentClassName in _classes))
     throw("xbObjects.js:_Classes::registerClass: parentClassName " + parentClassName + " not defined");
   // evaluating and caching the prototype object in registerClass
   // works so long as we are dealing with "normal" source files
   // where functions are created in the global context and then
   // statements executed. when evaling code blocks as in xbCOM,
   // this no longer works and we need to defer the prototype caching
   // to the defineClass method
   _classes[className] = { "classConstructor": null, "parentClassName": parentClassName };
 }
 _Classes.prototype.registerClass = registerClass;
 function defineClass(className, prototype_func)
 {
   var p;
   if (!className)
     throw("xbObjects.js:_Classes::defineClass: className not given");
   var classRef = _classes[className];
   if (!classRef)
     throw("xbObjects.js:_Classes::defineClass: className " + className + " not registered");
   if (classRef.classConstructor)
     return;
   classRef.classConstructor = eval( className );
   var childPrototype  = classRef.classConstructor.prototype;
   var parentClassName = classRef.parentClassName;
   if (parentClassName)
   {
     var parentClassRef = _classes[parentClassName];
     if (!parentClassRef)
       throw("xbObjects.js:_Classes::defineClass: parentClassName " + parentClassName + " not registered");
     if (!parentClassRef.classConstructor)
     {
       // force parent"s prototype to be created by creating a dummy instance
       // note constructor must handle "default" constructor case
       var dummy;
       eval("dummy = new " + parentClassName + "();");
     }
     var parentPrototype = parentClassRef.classConstructor.prototype;
     for (p in parentPrototype)
     {
       switch (p)
       {
       case "isa":
       case "classRef":
       case "parentPrototype":
       case "parentConstructor":
       case "inheritedFrom":
         break;
       default:
         childPrototype[p] = parentPrototype[p];
         break;
       }
     }
   }
   prototype_func();
   childPrototype.isa        = className;
   childPrototype.classRef   = classRef;
   // cache method implementor info
   childPrototype.inheritedFrom = new Object();
   if (parentClassName)
   {
     for (p in parentPrototype)
     {
       switch (p)
       {
       case "isa":
       case "classRef":
       case "parentPrototype":
       case "parentConstructor":
       case "inheritedFrom":
         break;
       default:
         if (childPrototype[p] == parentPrototype[p] && parentPrototype.inheritedFrom[p])
         {
           childPrototype.inheritedFrom[p] = parentPrototype.inheritedFrom[p];
         }
         else
         {
           childPrototype.inheritedFrom[p] = parentClassName;
         }
         break;
       }
     }
   }
 }
 _Classes.prototype.defineClass = defineClass;

} // create global instance var _classes = new _Classes(); // register root class xbObject _classes.registerClass("xbObject"); function xbObject() {

 _classes.defineClass("xbObject", _prototype_func);
 this.init();
 function _prototype_func()
 {
   // isa is set by defineClass() to the className
   // Note that this can change dynamically as the class is cast
   // into it"s ancestors...
   xbObject.prototype.isa        = null;
   // classref is set by defineClass() to point to the
   // _classes entry for this class. This allows access
   // the original _class"s entry no matter how it has
   // been recast.
   // *** This will never change!!!! ***
   xbObject.prototype.classRef      = null;
   xbObject.prototype.inheritedFrom = new Object();
   function init() { }
   xbObject.prototype.init        = init;
   function destroy() {}
   xbObject.prototype.destroy      = destroy;
   function parentMethod(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
   {
     // find who implemented this method
     var className       = this.isa;
     var parentClassName = _classes[className].classConstructor.prototype.inheritedFrom[method];
     var tempMethod      = _classes[parentClassName].classConstructor.prototype[method];
     // "cast" this into the implementor of the method
     // so that if parentMethod is called by the parent"s method,
     // the search for it"s implementor will start there and not
     // cause infinite recursion
     this.isa   = parentClassName;
     var retVal = tempMethod.call(this, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
     this.isa   = className;
     return retVal;
   }
   xbObject.prototype.parentMethod    = parentMethod;
   function isInstanceOf(otherClassConstructor)
   {
     var className = this.isa;
     var otherClassName = otherClassConstructor.prototype.isa;
     while (className)
     {
       if (className == otherClassName)
         return true;
       className = _classes[className].parentClassName;
     }
     return false;
   }
   xbObject.prototype.isInstanceOf    = isInstanceOf;
 }

} // eof: xbObjects.js

</script> </head> <body> <script type="text/javascript"> _classes.registerClass("Shape"); function Shape(iSides) {

   _classes.defineClass("Shape", prototypeFunction);
   this.init(iSides);
   function prototypeFunction() {
       Shape.prototype.init = function(iSides) {
           this.parentMethod("init");
           this.sides = iSides;
       };
       Shape.prototype.getArea = function () {
           return 0;
       };
   }

} _classes.registerClass("Triangle", "Shape"); function Triangle(iBase, iHeight) {

   _classes.defineClass("Triangle", prototypeFunction);
   this.init(iBase,iHeight);
   function prototypeFunction() {
       Triangle.prototype.init = function(iBase, iHeight) {
           this.parentMethod("init", 3);
           this.base = iBase;
           this.height = iHeight;
       };
       Triangle.prototype.getArea = function () {
           return 0.5 * this.base * this.height;
       };
   }

} _classes.registerClass("Rectangle", "Shape"); function Rectangle(iLength, iWidth) {

   _classes.defineClass("Rectangle", prototypeFunction);
   this.init(iLength, iWidth);
   function prototypeFunction() {
       Rectangle.prototype.init = function(iLength, iWidth) {
           this.parentMethod("init", 4);
           this.length = iLength;
           this.width = iWidth;
       }
       Rectangle.prototype.getArea = function () {
           return this.length * this.width;
       };
   }

} var triangle = new Triangle(10, 40); var rectangle = new Rectangle(20, 50); alert(triangle.sides); alert(triangle.getArea()); alert(rectangle.sides); alert(rectangle.getArea()); </script> </body> </html></source>


Using xbObjects to call the function from base class

   <source lang="javascript">

<html> <head> <title>Example</title> <script type="text/javascript"> /*

* xbObjects.js
* $Revision: 1.2 $ $Date: 2003/02/07 16:04:20 $
*/

/* ***** BEGIN LICENSE BLOCK *****

* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Bob Clary code.
*
* The Initial Developer of the Original Code is
* Bob Clary.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Bob Clary <bc@bclary.ru>
*
* ***** END LICENSE BLOCK ***** */

/* ChangeLog: 2001-12-19 - bclary - changed xbException init method to

          remove possible exception due to permission denied issues
          in gecko 0.9.5+
  • /

function _Classes() {

 if (typeof(_classes) != "undefined")
   throw("Only one instance of _Classes() can be created");
 function registerClass(className, parentClassName)
 {
   if (!className)
     throw("xbObjects.js:_Classes::registerClass: className missing");
   if (className in _classes)
     return;
   if (className != "xbObject" && !parentClassName)
     parentClassName = "xbObject";
   if (!parentClassName)
     parentClassName = null;
   else if ( !(parentClassName in _classes))
     throw("xbObjects.js:_Classes::registerClass: parentClassName " + parentClassName + " not defined");
   // evaluating and caching the prototype object in registerClass
   // works so long as we are dealing with "normal" source files
   // where functions are created in the global context and then
   // statements executed. when evaling code blocks as in xbCOM,
   // this no longer works and we need to defer the prototype caching
   // to the defineClass method
   _classes[className] = { "classConstructor": null, "parentClassName": parentClassName };
 }
 _Classes.prototype.registerClass = registerClass;
 function defineClass(className, prototype_func)
 {
   var p;
   if (!className)
     throw("xbObjects.js:_Classes::defineClass: className not given");
   var classRef = _classes[className];
   if (!classRef)
     throw("xbObjects.js:_Classes::defineClass: className " + className + " not registered");
   if (classRef.classConstructor)
     return;
   classRef.classConstructor = eval( className );
   var childPrototype  = classRef.classConstructor.prototype;
   var parentClassName = classRef.parentClassName;
   if (parentClassName)
   {
     var parentClassRef = _classes[parentClassName];
     if (!parentClassRef)
       throw("xbObjects.js:_Classes::defineClass: parentClassName " + parentClassName + " not registered");
     if (!parentClassRef.classConstructor)
     {
       // force parent"s prototype to be created by creating a dummy instance
       // note constructor must handle "default" constructor case
       var dummy;
       eval("dummy = new " + parentClassName + "();");
     }
     var parentPrototype = parentClassRef.classConstructor.prototype;
     for (p in parentPrototype)
     {
       switch (p)
       {
       case "isa":
       case "classRef":
       case "parentPrototype":
       case "parentConstructor":
       case "inheritedFrom":
         break;
       default:
         childPrototype[p] = parentPrototype[p];
         break;
       }
     }
   }
   prototype_func();
   childPrototype.isa        = className;
   childPrototype.classRef   = classRef;
   // cache method implementor info
   childPrototype.inheritedFrom = new Object();
   if (parentClassName)
   {
     for (p in parentPrototype)
     {
       switch (p)
       {
       case "isa":
       case "classRef":
       case "parentPrototype":
       case "parentConstructor":
       case "inheritedFrom":
         break;
       default:
         if (childPrototype[p] == parentPrototype[p] && parentPrototype.inheritedFrom[p])
         {
           childPrototype.inheritedFrom[p] = parentPrototype.inheritedFrom[p];
         }
         else
         {
           childPrototype.inheritedFrom[p] = parentClassName;
         }
         break;
       }
     }
   }
 }
 _Classes.prototype.defineClass = defineClass;

} // create global instance var _classes = new _Classes(); // register root class xbObject _classes.registerClass("xbObject"); function xbObject() {

 _classes.defineClass("xbObject", _prototype_func);
 this.init();
 function _prototype_func()
 {
   // isa is set by defineClass() to the className
   // Note that this can change dynamically as the class is cast
   // into it"s ancestors...
   xbObject.prototype.isa        = null;
   // classref is set by defineClass() to point to the
   // _classes entry for this class. This allows access
   // the original _class"s entry no matter how it has
   // been recast.
   // *** This will never change!!!! ***
   xbObject.prototype.classRef      = null;
   xbObject.prototype.inheritedFrom = new Object();
   function init() { }
   xbObject.prototype.init        = init;
   function destroy() {}
   xbObject.prototype.destroy      = destroy;
   function parentMethod(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
   {
     // find who implemented this method
     var className       = this.isa;
     var parentClassName = _classes[className].classConstructor.prototype.inheritedFrom[method];
     var tempMethod      = _classes[parentClassName].classConstructor.prototype[method];
     // "cast" this into the implementor of the method
     // so that if parentMethod is called by the parent"s method,
     // the search for it"s implementor will start there and not
     // cause infinite recursion
     this.isa   = parentClassName;
     var retVal = tempMethod.call(this, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
     this.isa   = className;
     return retVal;
   }
   xbObject.prototype.parentMethod    = parentMethod;
   function isInstanceOf(otherClassConstructor)
   {
     var className = this.isa;
     var otherClassName = otherClassConstructor.prototype.isa;
     while (className)
     {
       if (className == otherClassName)
         return true;
       className = _classes[className].parentClassName;
     }
     return false;
   }
   xbObject.prototype.isInstanceOf    = isInstanceOf;
 }

} // eof: xbObjects.js

</script> </head> <body> <script type="text/javascript"> _classes.registerClass("BaseClass"); function BaseClass(sColor) {

   _classes.defineClass("BaseClass", prototypeFunction);
   this.init(sColor);
   function prototypeFunction() {
       BaseClass.prototype.init = function (sColor) {
           this.parentMethod("init");
           this.color = sColor;
       };
       BaseClass.prototype.sayColor = function () {
           alert(this.color);
       };
   }

} var objA = new BaseClass("red"); objA.sayColor(); </script> </body> </html></source>