Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Writing Object-Oriented JavaScript Part 2

0.00/5 (No votes)
8 Dec 2003 4  
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:

  1. Class definition section
  2. Class instance section
  3. 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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here