Click here to Skip to main content
15,879,535 members
Articles / Web Development / HTML
Article

Writing Object-Oriented JavaScript Part 1

Rate me:
Please Sign up or sign in to vote.
4.85/5 (131 votes)
8 Dec 20037 min read 577.8K   3.3K   191   32
Using Cfx to develop a JavaScript control class library.

Introduction

ASP.NET and Visual Studio 7.0 are making important contributions to the improvement of the web development experience. Unfortunately, there is also a tendency created among developers to limit their interaction with JavaScript. Clearly JavaScript is valuable for adding client-side functionality to web pages. However ASP.NET programming models suggest that developers produce page layout while emitting client-side JavaScript from ASP.NET controls. As a consequence, this model tends to limit JavaScript to procedural adjuncts. This is rather unfortunate because it severely limits the power of an object-oriented scripting language that developers can use to write rich and reusable client-side components.

There are several reasons why JavaScript object-oriented capabilities are not fully utilized:

  • The tendency of client-side operations to be discrete favoring procedures.
  • ASP.NET programming model for its controls suggests limiting JavaScript to functional adjuncts.
  • Legacy JavaScript lacked key features such as exception handling and inner functions.
  • Text books on JavaScript often de-emphasize its support for object-oriented programming.
  • JavaScript does not formally support class inheritance like conventional OOP languages.

But writing JavaScript procedurally creates another set of undesirable conditions:

  • Task duplication due to the decoupling of data and functions.
  • Tendency to use complex array patterns to simulate abstract data types.
  • Increases the impact risk and side effects of code modifications.
  • Complicates auditing of functions thereby diminishing reusability.
  • The consequential effect is reduced performance.

We can enhance our scripts and eliminate these problems by writing JavaScript in an object-oriented fashion. JavaScript possess all the familiar object-oriented programming tenets, by its ability to define new object types and extend them through its prototype property. Prototypes serve as templates for an entire class of abstract data types. Therefore prototype properties are shared by all instantiated objects of the class. While this is a powerful and expressive type of inheritance model, what's missing is the customary class inheritance support that is familiar to programmers of conventional object-oriented languages.

Writing object-oriented JavaScript will be presented in a series of three articles. The first installment provides background on how JavaScript supports the main principles of object-oriented programming. Part two demonstrates how JavaScript constructs can be used to build a class inheritance framework and write scripts supporting a JavaScript class hierarchy. The third and final installment shows how to use the JavaScript class framework to build object-oriented client-side abstractions of ASP.NET user controls.

It is the aim of this series to introduce an alternate programming strategy and style that fully utilities JavaScript capabilities, and then elevates JavaScript from its typical junior partner role (reinforced by the ASP.NET programming model), into full domain partnership with ASP.NET. The result will enable developers to write rich client-side scripts.

Principles of Object-Oriented Programming in JavaScript

Abstract Data Types

Abstract data types are descriptions and representations of a real-world construct. Many languages support aggregation of disparate intrinsic data types to represent some real world construct. For example in "C" the struct keyword denotes such an aggregation. However an abstract data type is more than just an aggregation of disparate data types. An abstract data type also defines behaviors represented by the abstraction. In many object-oriented languages the combination of data and its associated behaviors denotes a class. Languages such as C++, Java, and C# provide the class keyword to identify abstract data types.

While JavaScript reserves the class keyword it does not support defining classes in the same manner as conventional OOP languages. In JavaScript functions are used as object descriptors and all functions are actually JavaScript objects. Thus in JavaScript, classes are function definitions. The code below illustrates how the simplest class is defined in JavaScript -- the empty class MyClass:

JavaScript
function MyClass() {}
Figure 1. Empty class definition

Using the JavaScript new operator defines myClassObj as an object instance of the type MyClass:

JavaScript
var myClassObj = new MyClass();
Figure 2. Object instantiation

Notice that the function keyword is overloaded and serves as both the constructor function for objects as well as identifying procedural functions:

JavaScript
var result = MyClass();
Figure 3. MyClass used as a procedural function

The difference between MyClass being interpreted as a constructor or as a procedure is the new operator. The new operator instantiates an object of class MyClass calling the constructor function, while in the second call a procedural call is made to the function MyClass expecting a return result.

MyClass defined in figure 1 is an empty class having no assigned data or behavior. JavaScript can dynamically add properties to the instantiated objects of MyClass:

JavaScript
var myClassObj = new MyClass();
myClassObj.myData = 5;
myClassObj.myString = "Hello World";
alert( myClassObj.myData );     // displays: 5
alert( myClassObj.myString );   // displays: "Hello World"
Figure 4. Dynamically assigning object properties

The problem however is that only the instance of MyClass referenced by myClassObj possesses the additional data properties. Subsequent instances will not have any properties. What is needed is a way to defined properties to all instances of MyClass. Using the this keyword in the constructor function data properties are now defined on all instances of MyClass:

JavaScript
function MyClass()
{
    this.myData = 5; 
    this.myString = "Hello World";
}

var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.myData = 10;
myClassObj1.myString = "Obj1:  Hello World";
myClassObj2.myData = 20;
myClassObj2.myString = "Obj2:  Hello World"; 
alert( myClassObj1.myData );    // displays: 10
alert( myClassObj1.myString );  // displays: "Obj1:  Hello World"
alert( myClassObj2.myData );    // displays: 20
alert( myClassObj2.myString );  // displays: "Obj2:  Hello World"
Figure 5. Defining data properties to all instances of the class

MyClass is still incomplete because there are no behaviors assigned to it. To add methods to MyClass, properties that reference functions are added to MyClass:

JavaScript
function MyClass()
{
    this.myData = 5;
    this.myString = "Hello World";
    this.ShowData = DisplayData;
    this.ShowString = DisplayString;
}

function DisplayData()
{
    alert( this.myData );
}

function DisplayString()
{
    alert( this.myString ); 
}

var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.myData = 10;
myClassObj1.myString = "Obj1:  Hello World";
myClassObj2.myData = 20;
myClassObj2.myString = "Obj2:  Hello World";
myClassObj1.ShowData();     // displays: 10
myClassObj1.ShowString();   // displays: "Obj1:  Hello World"
myClassObj2.ShowData();     // displays: 20
myClassObj2.ShowString();   // displays: "Obj2:  Hello World"
Figure 6. Defining data and methods to all instances of the class

The figure above defines a complete abstract data types or class in JavaScript. MyClass now defines a concrete type possessing data and associated behaviors.

Encapsulation

Using MyClass as defined above permits accessibility of its internal data representation as well as having its methods and variable names global in scope increasing the risk of name collisions. Encapsulation supports data hiding and the concept of viewing objects as self-contained entities providing services to consumers.

JavaScript
function MyClass()
{
    var m_data = 5;
    var m_text = "Hello World";
    this.SetData = SetData;
    this.SetText = SetText;
    this.ShowData = DisplayData;
    this.ShowText = DisplayText; 

    function DisplayData()
    {
        alert( m_data );
    }

    function DisplayText()
    {
        alert( m_text );
        return;
    }

    function SetData( myVal ) 
    {
        m_data = myVal;
    }

    function SetText( myText ) 
    {
        m_text = myText;
    }
}

var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.SetData( 10 );
myClassObj1.SetText( "Obj1:  Hello World" );
myClassObj2.SetData( 20 );
myClassObj2.SetText( "Obj2:  Hello World" );
myClassObj1.ShowData();    // displays: 10
myClassObj1.ShowText();    // displays: "Obj1:  Hello World"
myClassObj2.ShowData();    // displays: 20
myClassObj2.ShowText();    // displays: "Obj2:  Hello World"
Figure 7. Dynamically assigning properties to all object instances

JavaScript treats a class definition as a function definition and uses the var keyword to define local variables. Therefore var indicates that m_data is a local or "private" variable of MyClass. In figure 6, var was not used and thus, MyData is global in scope or "public". The var keyword is how encapsulation is specified in JavaScript.

Inheritance

JavaScript supports inheritance through the use of object prototypes. A prototype is a template of properties that is shared by all instances of the object type. Thus instances of object types "inherit" the values of its prototype property. In JavaScript, all object types have a prototype property that can be both extended and inherited.

In the example below, the Shape prototype object defines three properties, GetArea, GetPerimeter, and Draw, that reference the functions, Shape_GetArea, Shape_GetParameter, and Shape_Draw. Every instance of Shape inherits the prototype allowing them to call the Shape functions through the properties.

To achieve inheritance in JavaScript, the prototype of a previously defined object type is copied into the prototype of the derived object type. The Shape prototype below is copied into prototype of the derived Circle and Rectangle object types. Then the Draw property is overridden supplying the proper the function reference for the new class.

JavaScript
Shape.prototype.GetArea = Shape_GetArea; 
Shape.prototype.GetParameter = Shape_GetParameter;
Shape.prototype.Draw = Shape_Draw;

function Shape()
{
}

function Shape_GetArea()
{
    return this.area; 
}

function Shape_GetParameter()
{
    return this.parameter;
}

function Shape_Draw()
{
    alert( "Drawing generic shape" );
}

Circle.prototype = new Shape();
Circle.prototype.constructor = Circle;
Circle.prototype.baseClass = Shape.prototype.constructor;
Circle.prototype.Draw = Circle_Draw; 

function Circle( r )

{
    this.area = Math.PI * r * r;
    this.parameter = 2 * Math.PI * r;
}

function Circle_Draw()
{
    alert( "Drawing circle" );
}

Rectangle.prototype = new Shape();
Rectangle.prototype.constructor = Rectangle;

function Rectangle( x, y )

{
    this.area = x * y;
    this.parameter = 2 * x + 2 * y;
}

Rectangle.prototype = new Shape();
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.baseClass = Shape.prototype.constructor;
Rectangle.prototype.Draw = Rectangle_Draw;

function Rectangle( x, y )

{
    this.area = x * y; 
    this.parameter = 2 * x + 2 * y; 
}

function Rectangle_Draw()
{
    alert( "Drawing rectangle" );
}

var circle = new Circle( 10 );
var rectangle = new Rectangle( 10, 20 );

alert( "Circle base class = " + circle.baseClass );
alert( "Circle area = " + circle.GetArea() );
alert( "Circle parameter = " + circle.GetParameter() );
circle.Draw();

alert( "Rectangle base class = " + rectangle.baseClass );
alert( "Rectangle area = " + rectangle.GetArea() );
alert( " Rectangle parameter = " + rectangle.GetParameter() );
rectangle.Draw();
Figure 8. JavaScript Inheritance

Circle and Rectangle prototypes are assigned to the prototype of the Shape class through an object instance thereby "inheriting" the methods assigned to the prototype array of the Shape class. An instance of the Shape class is necessary to create a copy of the Shape prototype array. This permits overriding of the Shape methods yet preserving the original prototype array associated to all Shape objects.

Both the Circle and Shape classes extend the Shape class by overriding Draw method supplying its own respective implementation while inheriting the Shape implementation of the GetArea and GetParameter methods:

JavaScript
var circle = new Circle( 10 );
var rectandle = new Rectangle( 10, 20 );
alert( "Circle base class = " + circle.baseClass );
    // Alert:  Circle base class = <STRING constructor Shape of output>
alert( "Circle area = " + circle.GetArea() );
    // Alert: Circle area = 314.1592653589793
alert( "Circle parameter = " + circle.GetParameter() );
    // Alert: Circle parameter = 62.83185307179586
circle.Draw();
    // Alert: Drawing circle

alert( "Rectangle base class = " + rectangle.baseClass );
    // Alert:  Rectangle base class = <STRING constructor Shape of output>
alert( "Rectangle area = " + rectangle.GetArea() );
    // Alert: Rectangle area = 200
alert( "Rectangle parameter = " + rectangle.GetParameter() );
    // Alert: Rectangle parameter = 60
rectangle.Draw();
    // Alert: Drawing rectangle
Figure 9. Results of JavaScript Inheritance

Polymorphism

Polymorphism defines disparate behaviors and actions by objects to the same function invocation. The Draw property of Shape, Circle, and Rectangle object types is polymorphic. The Draw property invokes a different function depending upon its object type.

JavaScript
var shape = new Shape();
var circle = new Circle( 10 );
var rectandle = new Rectangle( 10, 20 );
shape.Draw();
    // Alert: Drawing generic shape
circle.Draw();
    // Alert: Drawing circle
rectangle.Draw();
    // Alert: Drawing rectangle
Figure 10. JavaScript polymorphism

Demo

The demo program, JsOOPDemo, is a standard ASP.NET application running only JavaScript with a blank web page as its UI. The URL for this site is http://localhost/JsOOP/JsOOPDemo/WebForm1.aspx. This means you should copy the source code into the JsOOP/JsOOPDemo 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 demo contains examples of the JavaScript found in the article and running the demo will help you explore writing object-oriented code in JavaScript.

Conclusion

JavaScript prototypes and function objects provide object-oriented features customary to object-oriented programming language. What is confusing is that JavaScript does not intrinsically support class inheritance and clumsily supports it through prototype inheritance. Part 2 of this series introduces a framework that simplifies JavaScript class inheritance and demonstrates writing a class hierarchy of JavaScript object types.

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


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionEncapsulation Pin
Wisen Technologies27-Jul-13 4:52
Wisen Technologies27-Jul-13 4:52 
GeneralMy vote of 4 Pin
Jai Mallesh20-Feb-13 20:33
Jai Mallesh20-Feb-13 20:33 
QuestionNice article... Pin
ssatheeshkumar25-Oct-12 17:02
ssatheeshkumar25-Oct-12 17:02 
GeneralMy vote of 5 Pin
Prashant L Raut11-Oct-12 0:05
Prashant L Raut11-Oct-12 0:05 
QuestionVery useful Pin
giuchici1-Jun-12 5:40
giuchici1-Jun-12 5:40 
GeneralReally great Article Pin
ravikiran from Hyderabad12-May-12 7:16
ravikiran from Hyderabad12-May-12 7:16 
GeneralMy vote of 5 Pin
Nikhil Pampatwar8-Feb-12 4:13
Nikhil Pampatwar8-Feb-12 4:13 
QuestionEncapsulation Pin
Pratzelwurm19-Oct-11 10:19
Pratzelwurm19-Oct-11 10:19 
GeneralMy vote of 3 Pin
fangpipig8-Jun-11 23:35
fangpipig8-Jun-11 23:35 
GeneralMy vote of 5 Pin
dl4gbe29-Jan-11 6:11
dl4gbe29-Jan-11 6:11 
GeneralMy vote of 5 Pin
Vishu_code_faster6-Jul-10 20:09
Vishu_code_faster6-Jul-10 20:09 
GeneralPretty nice Pin
Adnan Siddiqi7-Jan-09 22:12
Adnan Siddiqi7-Jan-09 22:12 
QuestionWhat a helpful article Pin
Member 42485610-Oct-08 6:28
Member 42485610-Oct-08 6:28 
GeneralThanks! Pin
User 126615721-Jun-07 9:57
User 126615721-Jun-07 9:57 
Generalparser error - JSoopdemo Pin
superjett29-Mar-07 7:11
superjett29-Mar-07 7:11 
GeneralJSOOP Library Pin
weerstra7-Jun-06 2:40
weerstra7-Jun-06 2:40 
GeneralTypo / Spelling Pin
Anonymous6-Jan-05 5:09
Anonymous6-Jan-05 5:09 
GeneralSide effects in OOJS Pin
darkoverlordofdata7-Jun-04 3:50
darkoverlordofdata7-Jun-04 3:50 
GeneralEsthetic question Pin
dcbrower15-Dec-03 5:09
dcbrower15-Dec-03 5:09 
Great! Your article is really helping me climb up the learning curve. My question involves the merit of using function literals to define methods in class prototypes. Instead of:
<br />
Circle.prototype.Draw = Circle_Draw; <br />
...<br />
function Circle_Draw()<br />
{<br />
    alert( "Drawing circle" );<br />
}<br />

I like this better, using a function literal:
<br />
Circle.prototype.Draw = function()<br />
{<br />
    alert( "Drawing circle" );<br />
}<br />

To me, this style better expresses the idea that Circle's Draw property is a method as opposed to an accessor. Defining Draw right there also keeps my eye from having to search the file for the definition of Circle_Draw. Finally, the code just looks more OOP-ish without the global Circle_Draw method cluttering the global namespace.

I'm pretty new to JavaScript, so maybe this style is unofficially frowned on? Or has actual disadvantages?

Can't wait to dive into Part 2...Smile | :)
GeneralRe: Esthetic question Pin
chriswa19-Dec-03 11:14
chriswa19-Dec-03 11:14 
GeneralGreat article! Pin
Rocky Moore14-Dec-03 15:41
Rocky Moore14-Dec-03 15:41 
GeneralRe: Great article! Pin
chriswa23-Dec-03 20:44
chriswa23-Dec-03 20:44 
GeneralInheritance Pin
Per Søderlind9-Dec-03 13:17
sussPer Søderlind9-Dec-03 13:17 
GeneralRe: Inheritance Pin
Per Søderlind9-Dec-03 13:24
sussPer Søderlind9-Dec-03 13:24 
GeneralRe: Inheritance Pin
worldspawn9-Dec-03 15:04
worldspawn9-Dec-03 15:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.