Writing Object-Oriented JavaScript Part 2






4.37/5 (21 votes)
Dec 9, 2003
8 min read

114404

1078
Using Cfx to develop a JavaScript control class library.
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 )
//Returns true if argument is an array; false otherwise.
IsDefined( arg )
//Returns true if argument is defined; false otherwise.
IsEmpty( obj )
//Returns true if an object posses no properties; false otherwise.
IsFunction( arg )
//Returns true if argument is a function; false otherwise.
IsNull( obj )
//Returns true if an object is a null; false otherwise.
IsObject( arg )
//Returns true if argument is an object; false otherwise.
IsString( arg )
//Returns true if argument is a string; false otherwise. |
Name |
FunctionName( func )
//Returns the name of the function.
ObjectName( obj )
//Returns the name of the object.
|
Conversion |
ToFunction( obj )
//Converts an object to a function.
|
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 )
//Returns the class name from the class argument.
IsDefined( thisClass )
//Returns true class is defined; false otherwise.
IsInitializing( thisClass )
//Returns true if class in initializing; false otherwise.s
//in initializing; false otherwise.
InstanceOf( thisClass, targClass )
//Determines if a class in an instance of target class
New( thisClass [,baseClassArg1, baseClassArg2, ..., baseClassArgN] )
//Defines a new class.
//Copies prototypes and properties from base class(es) to new class.
|
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 )
//Returns DOM element matched by the element id attribute.
FindElementByName( elementName )
//Returns DOM element matched by the element name attribute.
FindElementSetById( elementName )
//Returns a collection of DOM elements matched by the element id attribute.
FindElementSetByName( elementName )
//Returns a collection of DOM elements matched by
//the element name attribute.
GetElementById( elementId )
//Returns the DOM element of the elementId.
GetElementByName( elementName )
//Returns DOM element of the elementName.
GetElementTerms( argElement )
GetElementTerms( argElement, splitChar )
//Returns collection of element terms from the element id attribute.
|
Form |
GetForm()
GetForm( formIndex )
GetForm( formId )
//Returns document form.
|
Images |
FindImage( imageName )
//Returns DOM image matched by the element id attribute.
FindImageSet( imageName )
//Returns a collection of DOM images matched by the
//element id attribute.attribute.
GetImage( imageName )
//Returns a DOM image for the p; Returns a DOM image for
//the imageName id attribute.
|
Submit |
PostBack()
PostBack( formIndex )
PostBack( formIndex, eventTarget )
PostBack( formIndex, eventTarget, eventArgument )
PostBack( eventTarget )
PostBack( eventTarget, eventArgument )
//Submits form to server.
|
Properties: |
Browser |
isIE
//Returns true if browser is IE; false otherwise.
isNetscape
//Returns true if browser is Netscape; false otherwise.
isMozilla
//Returns true if browser is Mozilla; false otherwise.
|
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
//All objects inherit the className property.
//The className property for Object class is "Object".
baseClass
//Base class property – a base class or array of base classes.
//The baseClass property for Object class is null.
InstanceOf( targetClass )
//Returns true if this is an instance of targetClass; false otherwise.
InitInstance()
//Initializes class instance.; Initializes class instance.ance.;
//Initializes class instance.
|
Function |
Object representation of a function.className
//The className property for Function class is "Function".
baseClass
//The baseClass property for Function class is null.
instances
//The instances property informs class to keep track of class instances.
Method
Defines a new method on a function object.
|
Error |
Exception object.className
//The className property for Error class is "Error".
baseClass
//The baseClass property for Error class is Object.
|
Date |
Date and time object.className
//The className property for Date class is "Date".
baseClass
//The baseClass property for Date class is Object.
|
RegExp |
Regular Expression object.className
//The className property for String class is "RegExp".
baseClass
//The baseClass property for String class is Object.
|
String |
String object.className
//The className property for String class is "String".
baseClass
//The baseClass property for String class is Object.
trim()
//Removes leading and trailing whitespaces from strings.
|
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.
///////////////////////////////////////////////////////////////////////
// Class definition.
///////////////////////////////////////////////////////////////////////
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 );
// Define default properties.
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.
//////////////////////////////////////////////////////////////////////////
// Setup class instance.
/////////////////////////////////////////////////////////////////////////
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.
//////////////////////////////////////////////////////////////////////////
// Methods definitions.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// FieldName : fieldName accessor.
//////////////////////////////////////////////////////////////////////////
function FieldName()
{
// setter.
if ( arguments.length )
this.fieldName = arguments[0];
// getter.
return this.fieldName;
}
//////////////////////////////////////////////////////////////////////////
// Element : element accessor.
//////////////////////////////////////////////////////////////////////////
function Element()
{
// setter.
if ( arguments.length )
this.element = arguments[0];
// getter.
return this.element;
}
//////////////////////////////////////////////////////////////////////////
// Value : Value accessor.
//////////////////////////////////////////////////////////////////////////
function Value()
{
// setter.
if ( arguments.length )
this.element.value = arguments[0];
// getter.
return this.element.value;
}
//////////////////////////////////////////////////////////////////////////
// IsEnabled : returns whether the Element is enabled or disabled.
//////////////////////////////////////////////////////////////////////////
function IsEnabled()
{
return !this.element.disabled;
}
//////////////////////////////////////////////////////////////////////////
// Enable : enables or disables an element.
//////////////////////////////////////////////////////////////////////////
function Enable( state )
{
this.element.disabled = !state;
}
//////////////////////////////////////////////////////////////////////////
// SetFocus : sets the text element focus.
//////////////////////////////////////////////////////////////////////////
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 )
{
// Define members.
TextValidator.Method( AcceptChars );
// Define default properties.
TextValidator.acceptChars = "\\w";
return;
}
}
/////////////////////////////////////////////////////////////////////////
// Setup instance.
/////////////////////////////////////////////////////////////////////////
this.InitInstance()
if ( arguments.length )
{
this.fieldName = arguments[0];
this.element = Cfx.Dom.FindElementById( this.fieldName );
}
return this;
//////////////////////////////////////////////////////////////////////////
// Methods definitions
//////////////////////////////////////////////////////////////////////////
function AcceptChars()
{
// setter.
if ( arguments.length )
this.acceptChars = arguments[0];
// getter.
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()
{
/////////////////////////////////////////////////////////////////////////
// Class definition.
/////////////////////////////////////////////////////////////////////////
if ( Cfx.Class.IsDefined( this ) == false )
{
Cfx.Class.NewClass( WildcardValidator, TextValidator );
if ( Cfx.Class.IsInitializing( this ) )
{
// Define members.
WildcardValidator.Method( WildChar );
// Initialize class variables.
WildcardValidator.wildChar = "\*";
WildcardValidator.wildLimit = 4;
return;
}
}
/////////////////////////////////////////////////////////////////////////
// Setup instance.
/////////////////////////////////////////////////////////////////////////
if ( arguments.length )
{
this.fieldName = arguments[0];
this.element = Cfx.Dom.FindElement( this.fieldName );
}
return this;
//////////////////////////////////////////////////////////////////////
// Methods definitions
/////////////////////////////////////////////////////////////////////
function WildChar()
{
// setter.
if ( arguments.length )
this.wildChar = arguments[0];
// getter.
return Cfx.Js.IsDefined( this.wildChar ) ?
this.wildChar : WildcardValidator.wildChar;
}
function WildLimit()
{
// setter.
if ( arguments.length )
this.wildLimit = arguments[0];
// getter.
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
- Version 1.0