Introduction
Part 1 of this series discussed the object-oriented
features of JavaScript. What is unusual about JavaScript is its support of
prototype inheritance and the inelegant way that class inheritance is achieved
through prototype inheritance.
This installment introduces a framework that applies prototype inheritance in
a manner that simplifies writing class hierarchies in JavaScript. Using this
framework we can now easily define and derive new classes using a class
inheritance pattern very similar to conventional object-oriented languages.
The Strategy
Assigning Base Class Prototypes To Derived Class
To support class inheritance we need to construct a mechanism similar to a
virtual function table (vtable). Vtables are simulated by the framework through
JavaScript�s prototypes. Prototypes are essentially associative arrays that
describe a template of properties assigned to instances. Thus in JavaScript
"class inheritance"is simulated by assigning to the prototypes of a derived
class the prototype values from its base class. Once assigned, the derived class
can either override its prototype entries with its own implementation or default
to the implementation supplied to its prototype from the base class.
JavaScript Class Framework (Cfx)
The JavaScript class framework (Cfx
) simplifies writing class
hierarchies in JavaScript through a well-defined coding pattern. The framework
performs class inheritance by automatically assigning the base class prototype
and properties to its derived classes.
Cfx
is composed of a set of JavaScript objects offering services
that define new classes, provide useful DOM routines and JavaScript utilities.
These objects are encapsulated by the Cfx
object serving as the
overall namespace for the framework.
|
Figure 1. The JavaScript Class Framework (Cfx)
|
Cfx.Js
The Cfx.Js (JavaScript) object is a general utility object
encapsulating functions identifying object types. It also has functions that
extract the string name of an object or function and convert object types to
function types.
Type |
IsArray( arg )
IsDefined( arg )
IsEmpty( obj )
IsFunction( arg )
IsNull( obj )
IsObject( arg )
IsString( arg )
|
Name |
FunctionName( func )
ObjectName( obj )
|
Conversion |
ToFunction( obj )
|
Figure 2. Cfx.Js methods |
Cfx.Class
Cfx.Class provides the JavaScript class inheritance infrastructure.
New classes are defined using Cfx.Class.New
method. This method
assigns the prototype entries from base classes into the prototype array of the
derived class and permits the derived class prototype array to be inherited from
multiple base classes in reverse order of its argument list specification.
Therefore the class name positioned directly after the derived class in the
argument list represents the most significant base class, and is merely a
quick-and-dirty way of resolving inheritance conflicts.
GetName( classArg )
IsDefined( thisClass )
IsInitializing( thisClass )
InstanceOf( thisClass, targClass )
New( thisClass [,baseClassArg1, baseClassArg2, ..., baseClassArgN] )
|
Figure 3. Cfx.Class methods |
Cfx.Dom
Cfx.Dom
encapsulates the Document Object Model providing useful
methods for retrieving document items.
Methods: |
Element |
FindElementById( elementName )
FindElementByName( elementName )
FindElementSetById( elementName )
FindElementSetByName( elementName )
GetElementById( elementId )
GetElementByName( elementName )
GetElementTerms( argElement )
GetElementTerms( argElement, splitChar )
|
Form |
GetForm()
GetForm( formIndex )
GetForm( formId )
|
Images |
FindImage( imageName )
FindImageSet( imageName )
GetImage( imageName )
|
Submit |
PostBack()
PostBack( formIndex )
PostBack( formIndex, eventTarget )
PostBack( formIndex, eventTarget, eventArgument )
PostBack( eventTarget )
PostBack( eventTarget, eventArgument )
|
Properties: |
Browser |
isIE
isNetscape
isMozilla
|
Figure 4. Dom object property and methods
|
JavaScript Intrinsic Objects Extensions
In JavaScript all objects are extensible and Cfx
makes
full use of this feature. The framework extends the Function
object
providing the ability to add methods to a class. Object
is extended
with the className
and baseClass
properties. These
properties are used by Cfx to determine the class hierarchy for all instantiated
objects with Object
as the base class for all objects.
The Error
, Date
, and String
intrinsic
objects are extended with the className
and baseClass
properties defaulting to Object
as thier base class.
|
Figure 5. JavaScript Intrinsic Extensions in Cfx
|
For brevity, Error
, Date
, RegExp
, and
String
default properties and functions are not depicted, only
their extensions are depicted. In addition, Cfx
extends the
String
class with the trim
function to remove leading
and trailing whitespaces from strings. More information about JavaScript
intrinsic object extensions is provided below:
Object |
Root of all JavaScript objects.className
baseClass
InstanceOf( targetClass )
InitInstance()
|
Function |
Object representation of a function.className
baseClass
instances
Method
Defines a new method on a function object.
|
Error |
Exception object.className
baseClass
|
Date |
Date and time object.className
baseClass
|
RegExp |
Regular Expression object.className
baseClass
|
String |
String object.className
baseClass
trim()
|
Figure 6. Extension to JavaScript intrinsic objects
|
Developing A Class Hierarchy With Cfx
Using Cfx
, we define a class hierarchy and derived classes
refining the behavior of their base classes. For example, validating text input
elements is very common occurrence and why ASP.NET provides validators. However
a situation may arise where you need to write your own client-side validation
code for perhaps a specialized requirement. This section demonstrates how you
can define a class hierarchy of validators using Cfx
.
The following class diagram shows a class hierarchy of validators. The
Validator
class represents the base class for all derived
validators. The TextValidator
is derived from the
Validator
class and the WildcardValidator
class is
derived from the TextValidator
class.
|
Figure 7. Validator class hierarchy |
Writing A Class
The first class of interest is the Validator
class. It
represents the base class for all validator types. We can think of it as an
abstract class although JavaScript does not possess the concept of an abstract
class. Functions in JavaScript serves as both the object�s constructor function
and its class definition.
function Validator()
{
Using the following code pattern in the constructor function classes are
defined in the following three subsections:
- Class definition section
- Class instance section
- Method definition section
Class Definition Section
Class definition and initialization occurs in this block of the constructor
function. Cfx.Class.IsDefined
determines whether the
Validator
class (actually a JavaScript function object) has been
defined. If the class is not defined, Cfx.Class.New
is called to
create the Validator
class. During class initialization,
Cfx.Class.New
calls back the constructor function, detected by
Cfx.Class.IsInitializing
, to assign methods and properties to the
new class. Do not omit the return
statement from the
Cfx.Class.IsInitializing
code block. Control must return back to
Cfx.Class.New
to complete class construction.
if ( !Cfx.Class.IsDefined( Validator ) )
{
Cfx.Class.New( Validator );
if ( Cfx.Class.IsInitializing( Validator ) )
{
Validator.Method( FieldName );
Validator.Method( Element );
Validator.Method( Value );
Validator.Method( Enable );
Validator.Method( IsEnabled );
Validator.Method( SetFocus );
Validator.fieldName = null;
Validator.element = null;
return;
}
}
|
Figure 8. Validator class definition section |
Class Instance Section
In this section of the Validator
class constructor, there is no
need to initialize any instances since the class is not intended for
instantiation. This is how we designate that a class is abstract. In this case
the class instance is merely returned.
return this;
|
Figure 9. Validator class instance section |
Method Definition Section
The method definition section defines all the methods whose references are
assigned to the class in the class definition section. The class methods are
defined within the constructor function and are scoped to the class. This avoids
exposing the method names to the global namespace preventing name collisions.
function FieldName()
{
if ( arguments.length )
this.fieldName = arguments[0];
return this.fieldName;
}
function Element()
{
if ( arguments.length )
this.element = arguments[0];
return this.element;
}
function Value()
{
if ( arguments.length )
this.element.value = arguments[0];
return this.element.value;
}
function IsEnabled()
{
return !this.element.disabled;
}
function Enable( state )
{
this.element.disabled = !state;
}
function SetFocus()
{
if ( this.element != null )
{
if ( Cfx.Js.IsString( this.element.value ) )
{
this.element.focus();
if ( this.element.type == "text" )
this.element.select();
}
}
}
}
|
Figure 10. Validator method definition section |
Deriving classes
The TextValidator
class is derived using the same coding pattern
(class definition, class instance, and method definition section) as the
Validator
class. In its class definition section,
Cfx.Class.New
creates the TextValidator
class
inheriting the class methods and properties defined on the
Validator
class. The TextValidator
adds an accessor
method AcceptChars
used to get and set its acceptChars
property.
Cfx.Class.New( TextValidator, Validator );
Unlike the Validator
class, notice there is addition code in the
class instance section. This is because the TextValidator
class is
intended for instantiation. The InitInstance
method initializes
instances from its class object providing default values. Thus in the code shown
below, instances of the TextValidator
class obtain the
fieldName
, element
, and acceptChars
properties and thier default values from the TextValidator
class
object.
this.InitInstance();
Following the call to InitInstance
the constructor arguments are
inspected. If there are arguments they are used to override the default values
of the new instance. The last step is to return the instance from the
constructor.
if ( arguments.length )
{
this.fieldName = arguments[0];
this.element = Dom.FindElementById( this.fieldName );
}
return this;
The method definition section of the TextValidator
class defines
the AcceptChars
accessor method. All other methods and properties
of the TextValidator
class are inherited from the
Validator
class. Below is the complete definition of the
TextValidator
class.
function TextValidator()
{
/ Class definition.
if ( Cfx.Class.IsDefined( TextValidator ) == false )
{
Cfx.Class.New( TextValidator, Validator );
if ( Class.IsInitializing(.TextValidator )
{
TextValidator.Method( AcceptChars );
TextValidator.acceptChars = "\\w";
return;
}
}
this.InitInstance()
if ( arguments.length )
{
this.fieldName = arguments[0];
this.element = Cfx.Dom.FindElementById( this.fieldName );
}
return this;
function AcceptChars()
{
if ( arguments.length )
this.acceptChars = arguments[0];
return Cfx.Js.IsDefined( this.acceptChars ) ?
this.acceptChars : TextValidator.acceptChars;
}
}
|
Figure 11. TextValidator class definition |
Similarly WildcardValidator
is derived from
TextValidator
, adding additional methods and properties.
function WildcardValidator()
{
if ( Cfx.Class.IsDefined( this ) == false )
{
Cfx.Class.NewClass( WildcardValidator, TextValidator );
if ( Cfx.Class.IsInitializing( this ) )
{
WildcardValidator.Method( WildChar );
WildcardValidator.wildChar = "\*";
WildcardValidator.wildLimit = 4;
return;
}
}
if ( arguments.length )
{
this.fieldName = arguments[0];
this.element = Cfx.Dom.FindElement( this.fieldName );
}
return this;
function WildChar()
{
if ( arguments.length )
this.wildChar = arguments[0];
return Cfx.Js.IsDefined( this.wildChar ) ?
this.wildChar : WildcardValidator.wildChar;
}
function WildLimit()
{
if ( arguments.length )
this.wildLimit = arguments[0];
return Cfx.Js.IsDefined( this.wildLimit ) ?
this.wildChar : WildcardValidator.wildLimit;
}
}
|
Figure 12. WildcardValidator class definition |
Demo
The Inheritance
demo program, is an ASP.NET application using
Cfx
to build a JavaScript class hierarchy. The validators are
defined as wrappers of the text input elements displayed on the sample web page.
Using the validator objects we can access and modify the text input elements and
demonstrate class inheritance of the validators.
The URL for this site is http://localhost/JsOOP/Inheritance/WebForm1.aspx.
Copy the source code into the JsOOP/Inheritance
subdirectory of
your http://localhost home directory. You can use Visual Studio to create the
ASP.NET project or use the IIS management tool found in Administrative Tools of
the Control Panel. This demo has been tested on both version 1.0 and 1.1 of the
runtime.
The JavaScript class framework script file is included near the start of the
web page in WebForm1.aspx
followed by the script files of the
validation class hierarchy.
<script language="JavaScript"
src="scripts/Cfx.js"type="text/javascript"></script>
<script language="JavaScript"
src="scripts/Validator.js"type="text/javascript"></script>
<script language="JavaScript"
src="scripts/TextValidator.js"type="text/javascript"></script>
<script language="JavaScript"
src="scripts/WildcardValidator.js"type="text/javascript"></script>
In the OnLoad
function validation objects corresponding to the
text elements are created. A WildcardValidator
class object wraps
TextBox1
field and it is during this initial object creation that
the framework creates the entire Validator
class hierarchy. The
call to the InstanceOf
method from the valText1
object
demonstrates the creation of the class hierarchy by the framework.
alert( "in OnLoad");
var valText1 = new WildcardValidator( "TextBox1");
alert( valText1.FieldName() );
alert( valText1.ClassName() + "instance of Validator = "+
valText1.InstanceOf( Validator ) );
Objects of the TextValidator
class wrap TextBox2
and TextBox3
text elements. The following code demonstrates using
the methods defined by the class hierarchy to manipulate the text elements.
Observe how textbox3
is assigned the value of textBox2
through the TextValidator
objects valText2
and
valText3
.
var valText2 = new TextValidator( "TextBox2" );
valText2.Enable( false );
alert( valText2.FieldName() );
valText2.SetFocus();
alert( valText2.FieldName() );
var valText3 = new TextValidator( "TextBox3" );
alert( valText3.FieldName() );
valText3.Value( valText2.Value().trim() );
Conclusion
Using Cfx
, client-side scripts can now be written using a
familiar object-oriented design and style. While the objects presented here are
merely wrappers for text fields, they do an excellent job of encapsulating and
hiding implementation details. This can be very useful for hiding nuances among
the various browsers rendering your web pages, and most importantly enable code
reuse. Structuring your scripts this way also provides you the ability to design
richer client-side web pages enhancing the user experience of your web sites.
Also note that the naming conventions used in Cfx
are pascal casing
for method names and camel casing for property names. This helps to visually
distinguish between the two.
The final installment demonstrates how you can use this framework to develop
a class hierarchy of JavaScript control providing rich client-side functionality
for ASP.NET web pages.controls
.
History