Section 9.6. Extending Without Inheriting


9.6. Extending Without Inheriting

The discussion of creating subclasses earlier in this chapter explains how to create a new class that inherits the methods of another. JavaScript is such a flexible language that subclassing and inheritance is not the only way to extend a class. Since JavaScript functions are data values, you can simply copy (or "borrow") the functions from one class for use in another. Example 9-4 shows a function that borrows all the methods in one class and makes copies in the prototype object of another class.

Example 9-4. Borrowing methods from one class for use by another

 // Borrow methods from one class for use by another. // The arguments should be the constructor functions for the classes. // Methods of built-in types such as Object, Array, Date, and RegExp are // not enumerable and cannot be borrowed with this method. function borrowMethods(borrowFrom, addTo) {     var from = borrowFrom.prototype;  // prototype object to borrow from     var to = addTo.prototype;         // prototype object to extend     for(m in from) {  // Loop through all properties of the prototye         if (typeof from[m] != "function") continue; // ignore nonfunctions         to[m] = from[m];  // borrow the method     } } 

Many methods are tied strongly to the class that defines them, and it makes no sense to try to use them in other classes. But it is possible to write some methods generically so that they are suitable for use by any class, or by any class that defines certain properties. Example 9-5 includes two classes that do nothing but define useful methods that other classes can borrow. Classes like these that are designed for borrowing are called mixin classes or mixins.

Example 9-5. Mixin classes with generic methods for borrowing

 // This class isn't good for much on its own. But it does define a // generic toString( ) method that may be of interest to other classes. function GenericToString( ) {} GenericToString.prototype.toString = function( ) {     var props = [];     for(var name in this) {         if (!this.hasOwnProperty(name)) continue;         var value = this[name];         var s = name + ":"         switch(typeof value) {         case 'function':             s += "function";             break;         case 'object':             if (value instanceof Array) s += "array"             else s += value.toString( );             break;         default:             s += String(value);             break;         }         props.push(s);     }     return "{" + props.join(", ") + "}"; } // This mixin class defines an equals( ) method that can compare // simple objects for equality. function GenericEquals( ) {} GenericEquals.prototype.equals = function(that) {     if (this == that) return true;     // this and that are equal only if this has all the properties of     // that and doesn't have any additional properties     // Note that we don't do deep comparison.  Property values     // must be === to each other.  So properties that refer to objects     // must refer to the same object, not objects that are equals( )     var propsInThat = 0;     for(var name in that) {         propsInThat++;         if (this[name] !== that[name]) return false;     }     // Now make sure that this object doesn't have additional props     var propsInThis = 0;     for(name in this) propsInThis++;     // If this has additional properties, then they are not equal     if (propsInThis != propsInThat) return false;     // The two objects appear to be equal.     return true; } 

Here is a simple Rectangle class that borrows the toString( ) and equals( ) methods defined by the mixin classes:

 // Here is a simple Rectangle class. function Rectangle(x, y, w, h) {     this.x = x;     this.y = y;     this.width = w;     this.height = h; } Rectangle.prototype.area = function( ) { return this.width * this.height; } // Borrow some more methods for it borrowMethods(GenericEquals, Rectangle); borrowMethods(GenericToString, Rectangle); 

Neither of the mixins shown have a constructor function, but it is possible to borrow constructors as well. In the following code, a new class is created named ColoredRectangle. It inherits rectangle functionality from Rectangle and borrows a constructor and a method from a mixin named Colored:

 // This mixin has a method that depends on its constructor.  Both the // constructor and the method must be borrowed. function Colored(c) { this.color = c; } Colored.prototype.getColor = function( ) { return this.color; } // Define the constructor for a new class. function ColoredRectangle(x, y, w, h, c) {     this.superclass(x, y, w, h);  // Invoke superclass constructor     Colored.call(this, c);        // and borrow the Colored constructor } // Set up the prototype object to inherit methods from Rectangle ColoredRectangle.prototype = new Rectangle( ); ColoredRectangle.prototype.constructor = ColoredRectangle; ColoredRectangle.prototype.superclass = Rectangle; // And borrow the methods of Colored for our new class borrowMethods(Colored, ColoredRectangle); 

The ColoredRectangle class extends (and inherits methods from) Rectangle and borrows methods from Colored. Rectangle itself inherits from Object and borrows from GenericEquals and GenericToString. Although any kind of strict analogy is impossible, you can think of this as a kind of multiple inheritance. Since the ColoredRectangle class borrows the methods of Colored, instances of ColoredRectangle can be considered instances of Colored as well. The instanceof operator will not report this, but in Section 9.7.3., we'll develop a more general method for determining whether an object inherits from or borrows from a specified class.




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net