|
The Gravey 2.5 Framework | |||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||
Alternate Collection of routines to emulate multiple-inheritance
classes in JavaScript. This "multiple inheritance" has the earlier
classes in the superclass list getting precedence over the later classes
with regard to resolving both explicit and implicit-via-super references.
A detailed explanation of the functionality of this file can
be found in the AJAX from Scratch
series of articles.
NOTE: Include this grvClasses.js file OR grvClass.js, but not both!
Version: 2.2
Requires:
| Class Summary | |
| GrvObject | This is the root class for all "classes" implemented via the GLOBALS.Class() function (ala Java "Object"). |
| Method Summary | |
static void
|
grvImplements()
This utility function sets up multiple "implements" inheritance with all of the interfaces/classes passed in as parameters. |
// This file uses JSDoc-friendly comments [ http://jsdoc.sourceforge.net/ ] /** * @file grvClasses.js * @fileoverview Alternate Collection of routines to emulate multiple-inheritance * classes in JavaScript. This "multiple inheritance" has the earlier * classes in the superclass list getting precedence over the later classes * with regard to resolving both explicit and implicit-via-super references. * A detailed explanation of the functionality of this file can * be found in the <a target="_blank" * href="http://www.polyglotinc.com/AJAXscratch/Class">AJAX from Scratch</a> * series of articles.<br/> * <b>NOTE:</b> Include this grvClasses.js file OR grvClass.js, but not both! * * @author Bruce Wallace (PolyGlotInc.com) * @version 2.2 * @requires grvValidate.js (optional; only required if validating constructor args) */ ////////////////////////////////////////////////////////////////// // GRAVEY LEXICAL CODING CONVENTIONS: // (*) All private variables or functions start with "_" // (*) All variables and functions start with a lowercase letter // (*) All Classes start with an uppercase letter // (*) All Class methods and instance variables start with lowercase // (*) All Class "static" methods and variables start with uppercase // (*) All constants start with "k" // (*) All global variables start with "g" // (*) All event handler functions start with "on" // // (*) All Gravey utility global variables start with "gGrv" // (*) All Gravey MVC global variables start with "gMVC" // (*) All Gravey EDO global variables start with "gEDO" // (*) All Gravey utility functions start with "grv" // (*) All Gravey MVC functions start with "mvc" // (*) All Gravey MVC event handler functions start with "onMVC" // (*) All Gravey EDO event handler functions start with "onEDO" // (*) All Gravey MVC classes start with "MVC" // (*) All Gravey EDO classes start with "EDO" ////////////////////////////////////////////////////////////////// /** * This function returns the name of a given function; It does this by * using a regular expression to extract the function name from the * function source code. * @param {Function} f reference to a function * @return name of given function * @type String * @author Bruce Wallace (PolyGlotInc.com) * @version 2.0 */ function grvFuncName(f) { var s = f.toString().match(/function\s*(\S*)\s*\(/)[1]; return s ? s : "anonymous"; } Class(GrvObject); /** * @class This is the root class for all "classes" implemented * via the {@link GLOBALS#Class} function (ala Java "Object"). * Class replaces the normal Javascript "constructor" function * with a new wrapper function that implements a robust * inheritence mechanism. * This mechanism supports "pretty" source code whereby * "methods" are declared inline, but the source code of * those methods are not copied into every object instance * as would otherwise be the case.<p> * The wrapper function also sets up a real "constructor" * that can use {@link GLOBALS#grvValidateArgs} to insure * that the required parameters are passed to it, throwing * an exception if not.<p> * The design pattern implemented by Class/Extends corrects * several problems that occur with the naive inheritence * mechanisms often used with Javascript. *<pre> * The required coding convention for "GrvObject": * (1) the "class" is declared via the utility * function {@link GLOBALS#Class} * (2) the "superclass" (if any) is declared via the * .Extends method {@link GLOBALS#grvExtends} * (3) the "constructor" logic for the class is placed * in a "method" named "konstructor" * (4) the konstructor method invokes the "super" constructor * via its class name [rather than literally "super()"] * (5) "interfaces" and extra "superclasses" (if any) are * declared via the .Implements method {@link GLOBALS#grvImplements} *</pre> * @throws grvMissingArgException thrown by constructor wrapper * @see GLOBALS#Class * @see GLOBALS#grvExtends * @see GLOBALS#grvImplements * @author Bruce Wallace (PolyGlotInc.com) * @version 2.2 */ function GrvObject() { /** Invokes the "super" version of whatever method that is calling souper. * @return either the return value of the super method or null if no super exists */ this.souper = function() { var superMethod = this.souper.caller.souper; return (superMethod) ? superMethod.apply( this, arguments ) : null; } /** Invokes the version of whatever method that is calling souper_ from * the specified superclass. * @return either the return value of the super method or null if no super exists */ this.souper_ = function( superclass ) { var superMethod = this[superclass.cname]._[this.souper_.caller.mname]; var theArgs = Array.prototype.slice.call(arguments,1); return (superMethod) ? superMethod.apply( this, theArgs ) : null; } /** Is "this" a descendent of the given class?. * This implementation works with Gravey's multiple inheritence. * @param {Function} aClass the class to verify * @return true iff "this" is an "instanceof" aClass */ this.isInstanceOf = function( aClass ) { if (aClass.cname==null) return false; var m = this[ aClass.cname ]; return( m && m instanceof Function ); } } /** * This utility function sets up multiple "implements" inheritance * with all of the interfaces/classes passed in as parameters. * Multiple inheritance is implemented where method references * (either explicit or implicit-via-super) are resolved with * left-to-right precedence (with "Extends" superclass getting highest) * @see #Class * @see GrvObject * @author Bruce Wallace (PolyGlotInc.com) * @version 2.2 */ function grvImplements() { /////////////////////////////////////////////////////////////////// // NOTE: At this point each superClass should have already had its // own Class and Extends calls performed. I.E. always "declare" // superclasses BEFORE any classes that extend them. /////////////////////////////////////////////////////////////////// var N = arguments.length; for (var i=0; i<N; ++i) if (!arguments[i].baseClass) throw "Undeclared Super Class in .Implements"; var s, m, superExemplar; var rawExemplar = this.rawExemplar; var exemplar = this.prototype; //foreach superclass, zip together "souper" links //to each ORIGINAL subclass method not already souper-linked. // //I.E. foreach superclass, // foreach method in RAW exemplar, // if method in CURRENT exemplar doesnt already have a souper link // then if method exists in current superclass exemplar // then zip together "souper" reference for (var i=0; i<N; ++i) { superExemplar = arguments[i].prototype; for (var property in rawExemplar) { if (!(rawExemplar[property] instanceof Function)) continue; m = exemplar[property]; if (m.souper) continue; //already linked! s = superExemplar[property]; if (s && (s instanceof Function)) m.souper = s; } } //foreach superclass, copy into exemplar a reference //to each superclass method not already in exemplar for (var i=0; i<N; ++i) { superExemplar = arguments[i].prototype; for (var property in superExemplar) { s = superExemplar[property]; if ( exemplar[property]===void 0 && s instanceof Function ) exemplar[property] = s; } } } /** * This utility function sets up single inheritance. * @param {Function} superClass the "superclass constructor" function * @return the "class object" (an extended Function object) * @author Bruce Wallace (PolyGlotInc.com) * @version 2.2 */ function grvExtends( superClass ) { /////////////////////////////////////////////////////////////////// // NOTE: At this point superClass should have already had its own // Class and Extends calls performed. I.E. "declare" superclasses // BEFORE those classes that extend them. /////////////////////////////////////////////////////////////////// if (!superClass.baseClass) throw "Undeclared Super Class in .Extends"; //get superclass exemplar but avoid infinite recursion on implied root class var superexemplar = (this==GrvObject) ? null : (superClass ? superClass.prototype : null); //Recall that one can't change the exemplar.__proto__ property directly //SO, one must create a new exemplar instance after baseClass.prototype is set. this.baseClass.prototype = superexemplar; var exemplar = new this.baseClass(); //for each method in exemplar, if there is a different method with the //same name in superexemplar, setup the .souper link between them. //To support multiple inheritence disambiguation, save the method name too. var s,m; for (var property in exemplar)// iterate over all properties { m = exemplar[property]; if (!(m instanceof Function)) continue; m.mname = property; if (! superexemplar ) continue; s = superexemplar[property]; if (s && (s!=m) && (s instanceof Function)) m.souper = s; } //Note that it would normally be a problem if Extends were called after //someone has already created an instance of "this" class (via new). //However, that situation should never occur since all the calls to Class() //and Extends() are normally done at web page load time, and application //logic should only start after the page "onload" event, hence all these //exemplars should be stable. this.prototype = exemplar; exemplar.name = this.cname;//default value // while "souper" works with konstructor, some may still prefer to // use the superclass name explicitly when invoking its constructor // so we leave this in to continue support for that. if (exemplar.konstructor==null) exemplar.konstructor = function(){}; exemplar[this.cname] = exemplar.konstructor; // and it is required for multiple-inheritence to resolve ambiguity exemplar.konstructor._ = exemplar; //save the original (unextended) exemplar to support .Implements if (superClass==GrvObject) this.rawExemplar = exemplar; return this; } /** * Declare a class and create the glue objects and code * to allow "pretty" source; A mechanism is set up to insure * that required constructor parameters each have a defined value; * This function also defines the "name" attribute of the specified * class and initializes it with the class name. * @param {Function} theClass the "class constructor" function * @param {StringArray} optConstructorArgDescArray optional array * of strings describing required parameters for this class' * "constructor"; Note: Optional constructor parameters should * not be included in this array. * @return the "class object" (an extended Function object) * @type Function * @see #Extends * @see #grvValidateArgs * @see GrvObject * @author Bruce Wallace (PolyGlotInc.com) * @version 2.2 */ function Class( theClass, optConstructorArgDescArray ) { // The "class" as passed in will really be an abstract base class. We // will squirrel that away and replace theClass with a wrapper function // so that method source code is not copied into each object instance // as would otherwise be the case. var baseClass = theClass; // create the new wrapper function to replace theClass var className = grvFuncName( theClass ); self[className] = theClass = function(){ var C = arguments.callee; if (C.required && self["grvValidateArgs"]!==void 0) grvValidateArgs( C.cname, C.required, arguments ); C.prototype.konstructor.apply( this, arguments ); } theClass.required = optConstructorArgDescArray; theClass.cname = className; //FYI,netscape&firefox break with ".name" theClass.baseClass = baseClass; theClass.Extends = grvExtends;//so we can call as methods... theClass.Implements= grvImplements; theClass.Extends(GrvObject); //required here even if overridden later return theClass; }
|
The Gravey 2.5 Framework | |||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||