Object Oriented JavaScript using Prototype Object

For updated version of the article please visit www.agnosticdevs.com

Object Oriented JavaScript using Prototype Object

 

This document describes the benefit of using Object Oriented Approach in JavaScript Codes and implements Calendar Management component having a few functions like Add/Edit/Delete Events on selected date.

 

As we all know Object Oriented Programming (OOP) is a major part and practice for reusability. Most of the times even if we are following standard ways to code on server-side, we just forget or omit about client side structure of application. As a result it becomes hectic to manage later, when application is modified to release a newer version of the same.

 

As per my personal experience on the same, I found prototyping and object oriented JavaScript a perfect solution. There may be a lot of other ways as well, but this article will discuss about prototyping and inheritance in JavaScript.

 

Note: To understand the logic completely, you must have clear understanding on concepts like, Inheritance, constructors, Classes and objects. This will help you to understand the approach in this article better.

 

What is Prototype?

 

Prototyping is a concept taken from C++, the way of declaring the variable in Class and initializing it outside the class, using SCOPE RESOLUTION Operator (::). In the same way in JavaScript, prototype object can be treated as a way to implement the logic of scope resolution operator.

 

For Example:-

 

C++

class X
{
  public:
      static int count;
};
<?xml:namespace prefix = o /> 
int X::count = 10;                // define static data member

 

JavaScript

function X()

{

    X.prototype.count = 0;  /// this is equivalent to writing it outside X

}

 

X.prototype.count = 10;  ///initialize the count outside X

 

Note: prototype is static part of JavaScript Classes.

 

 

 

There are many advantages of using this approach, while coding big applications having complex Client Side Data Handling and manipulation, e.g. Ajax, Rich Text Editing, Slide Shows, Video Show-Reels, Light Box or Light Shows etc. Here I have listed a few:

 

  1. Structured: The Coding for any functionality can be structured in a very easily understandable way. For example, any one may co-relate the two complex defined Entities by a way of having familiarity to OOPs.

  2. Reusable: The Class components created using this approach can be readily reused in any other application with nominal changes.

  3. Plug and Play: As components can be reused for other applications, this is very easy to merge two components e.g, a Spell check component can be included in Rich Text Editor.

 

How it works:

 

In JavaScript, Classes are functions or in simple words: to declare a class and to declare a function the same syntax is used.

 

Example 1:

function Vehicle(_Model, _Make, _Color, _IsFourWheelDrive, _Mileage) {

    //public variables for class

    this.Model = _Model;

    this.Make = _Make;

    this.Color = _Color;

    this.IsFourWheelDrive = _IsFourWheelDrive;

    this.Mileage = _Mileage;

    this.Acceleration = 0;

 

    /// Private variables for Class

    var EngineNumber = "ABC0000" + this.Make;

}

 

///Common function for all Instances

Vehicle.prototype.Accelerate = function(_Acceleration) {

    this.Acceleration = _Acceleration;

}

 

var VehMiniCooper = new Vehicle("Mini", "Cooper", "White", false, 10);

VehMiniCooper.Accelerate(160);

 

Example 2:

var Vehicle = function() { };

Vehicle.prototype = {

    Initialize: function(_Model, _Make, _Color, _IsFourWheelDrive, _Mileage) {

        this.Model = _Model;

        this.Make = _Make;

        this.Color = _Color;

        this.IsFourWheelDrive = _IsFourWheelDrive;

        this.Mileage = _Mileage;

        this.Acceleration = 0;

 

        /// Private variables for Class

        var EngineNumber = "ABC0000" + this.Make;

    },

    Accelerate: function(_Acceleration) {

        this.Acceleration = _Acceleration;

    }

};

 

var VehMiniCooper = new Vehicle();

VehMiniCooper.Initialize("Mini", "Cooper", "White", false, 10);

VehMiniCooper.Accelerate(30);

 

Here both code snippets implement the same logic for vehicle class, but with two different syntaxes.

 

The prime logic behind not defining the Accelerate() function inside Vehicle class is to keep and use only one instance of function Accelerate() for instances of class Vehicle. This will save memory and the same logic will be implemented for all instances automatically.

 

In the above code we have different code blocks, (private and public). The variables defined with this.xxxxx are public variables and are accessible outside the class using object of the same class. The variable defined directly using var keyword are private variables accessible only inside the class.

 

The functions or variables are defined with classname.prototype, are static in scope and are initialized only once.

 

Our Example of Calendar:

 

We will learn the following in the implementation of Calendar:

 

  1. Implementation of Enumerations in JavaScript.

  2. Extending Predefined DataTypes such as Date.

  3. Creating Custom Classes in JavaScript.

  4. Using all the above together to write a complex logic simply.

 

Please refer the code with this article to follow up:

 

  1. Implementation of Enumerations in JavaScript:

 

Enumeration is a basic data type in any language, which has all its possible values pre-defined with it. This type cannot store any other value other then specified list with it.

 

JavaScript does not support this kind of DataTypes by default. But we can create the same manually to make it simpler for ourselves.

 

 

 

 

In our sample calendar, we will surely need Enumerations for Day and Month Names. So for the same, we may code like this:

var DayOfWeek = {

    Sunday: 0,

    Monday: 1,

    Tuesday: 2,

    Wednesday: 3,

    Thursday: 4,

    Friday: 5,

    Saturday: 6,

    GetDayName: function(iDay) {

        switch (iDay) {

            case 0:

                return "Sunday";

                break;

            case 1:

                return "Monday";

                break;

            case 2:

                return "Tuesday";

                break;

            case 3:

                return "Wednesday";

                break;

            case 4:

                return "Thursday";

                break;

            case 5:

                return "Friday";

                break;

            case 6:

                return "Saturday";

                break;

        }

    }

};

 

var NamesOfMonth = {

    January: 1,

    February: 2,

    March: 3,

    April: 4,

    May: 5,

    June: 6,

    July: 7,

    August: 8,

    September: 9,

    October: 10,

    November: 11,

    December: 12,

    GetMonthName: function(iMonth) {

        switch (iMonth.toString()) {

            case "1":

                return "January";

                break;

            case "2":

                return "February";

                break;

            case "3":

                return "March";

                break;

            case "4":

                return "April";

                break;

            case "5":

                return "May";

                break;

            case "6":

                return "June";

                break;

            case "7":

                return "July";

                break;

            case "8":

                return "August";

                break;

            case "9":

                return "September";

                break;

            case "10":

                return "October";

                break;

            case "11":

                return "November";

                break;

            case "12":

                return "December";

                break;

        }

    }

};

 

 

In the above code snippet, we have DayOfWeek and NamesOfMonth as our datatypes. The above type can be used as:

 

var Cal = new CalendarHelper(DayOfWeek.Wednesday, "DivCalendar");

Cal.GetCalendar(NamesOfMonth.October, 2009);

 

Here, the CalendarHelper is the class defined in the .js file and DayOfWeek.Wednesday refers to Numeric value 3, similarly NamesOfMonth.October refers to Numeric value 10.

 

  1. Extending Predefined DataTypes such as Date.

 

Extending itself means, adding on some functionality to existing classes. It’s similar to inheritance but still have some difference. Inheritance is to create a new class to add some functionality and reuse the rest as it is. On the other hand Extending means just to add new functionality to it.

 

In JavaScript, Both of the above terminologies can be implemented logically:

 

 

 

 

Inheritance:

var ExtendedDate = function() { };

ExtendedDate.prototype = Date.prototype;  // inheritance using Prototype Object

ExtendedDate.prototype.Initialize = function() {

    ///Your Implementation of own logic

    this.getDate(); /// here, this is the instance of current object of New Extended Class ExtendedDate

};

 

Extension:

Date.prototype.DaysInMonth = function(iMonth, iYear) {

    return 32 - new Date(iYear, iMonth, 32).getDate();

}

 

Date.prototype.Initialize = function(iYear, iMonth, iDay) {

    this.setFullYear(iYear, iMonth, iDay);

    this.DayOfWeek = this.getDayName();

    return this;

};

 

In above code samples, ExtendedDate is new inherited Class from Date and the other snippet simply shows the extended version of Date class with 2 functions DayInMonth and Initialize.

 

  1. Creating Custom Classes in JavaScript.

 

Other then Inheritance and Extension, we can also create Custom Classes to implement Complex Logics.

 

Here in the attached Sample, we have CalendarHelper Class created to render monthly calendar with all events marked in the same.

 

var CalendarHelper = function(StartDayOfWeek, Container) {

    this._StartDayOfWeek = StartDayOfWeek;

    this._Container = Container;

};

 

As described earlier in the article, functions are treated as classes in JavaScript. The above snippet initializes the CalendarHelper Object with reference of a Class having _StartDayOfWeek and _Container Properties.

 

Further this class can be extended to have more features such as:

 

  1. InitializeTemplates

  2. HandleEvents

  3. InitializeEvents

  4. AddEvent

  5. GetCalendar

 

 

 

Example:

CalendarHelper.prototype.GetCalendar = function(Month, Year) {

    this._Month = Month;

    this._Year = Year;

    this.InitializeTemplates();

    document.currentCalObj = this;

    $.post("AjaxCallerService.axd", { method: "GetAllEventsForMonth", month: Month, year: Year }, this.HandleEvents);

}

 

Please refer to code samples for whole implementation of the above sample.

 

  1. Using all the above together to write a complex logic simply.

 

  1. First of all the Constructor of Calendar class to initialize Start day of the Week and Container where to render the Calendar.

var CalendarHelper = function(StartDayOfWeek, Container) {

    this._StartDayOfWeek = StartDayOfWeek;

    this._Container = Container;

};

 

  1. Now, we need to define the basic HTML Templates for the Calendar in InitializeTemplates Method of Calendar.

CalendarHelper.prototype.InitializeTemplates = function() {

    this.CalendarTemplate = new String("<TABLE CLASS=\"Calendar\" CELLSPACING=\"0\">{THeads}{TBodys}{TFoot}</TABLE>");

    this.THeadTemplate = new String("<THEAD><TR><TH><A HREF=\"javascript:void(0);\" REL=\"{PreviousYear}\" REV=\"{PreviousMonth}\" ID=\"BtnPrevious\">&laquo;</A></TH></TH><TH COLSPAN=\"5\" CLASS=\"HeaderTitle\">{MonthName}, {Year}</TH><TH><A HREF=\"javascript:void(0);\" REL=\"{NextYear}\" REV=\"{NextMonth}\" ID=\"BtnNext\">&raquo;</A></TH></TR><TR>{Heads}</TR></THEAD>");

    this.BodyTemplate = new String("<TBODY>{TRs}</TBODY>");

    this.TrTemplate = new String("<TR>{TDs}</TR>");

    this.HeadTemplate = new String("<TH>{Data}</TH>");

    this.TDTemplate = new String("<TD>{Data}</TD>");

    this.TFootTemplate = new String("<TFOOT><TR>{Heads}</TR></TFOOT>");

    this.BlankTDTemplate = new String("<TD CLASS=\"padding\" COLSPAN=\"{Colspan}\"></TD>");

    this.TodaysDateTemplate = new String("<TD CLASS=\"today\">{Data}</TD>");

    this.EventTemplate = new String("<LI><SPAN CLASS=\"title\">{Dated}</SPAN><SPAN CLASS=\"desc\">{Description}</SPAN></LI>");

    this.EventDateTemplate = new String("<TD CLASS=\"date_has_event\">{Data}<DIV CLASS=\"events\"><UL>{Events}</UL></DIV></TD>");

}

 

This will help us to maintain Rendering logic flexible enough to make any changes in the layout whenever some design changes are required.

 

  1. Define the function to get calendar for specific month:

CalendarHelper.prototype.GetCalendar = function(Month, Year) {

    this._Month = Month;

    this._Year = Year;

    this.InitializeTemplates();

    document.currentCalObj = this;

    $.post("AjaxCallerService.axd", { method: "GetAllEventsForMonth", month: Month, year: Year }, this.HandleEvents);

}

This will call the function to initialize Templates and initiate a call to Server for Events. The JQuery Method $.post makes the call to IhttpHandler AjaxCallerService.axd and attaches HandleEvents method to Async Callback to handle JSON response from the same.

  1. Handle JSON Response

CalendarHelper.prototype.HandleEvents = function(Data) {

    var Events = eval(Data);

    var C = document.currentCalObj;

    C._Events = new Array();

    for (var i = 0; i < Events.length; i++) {

        var EventDate = eval(Events[i].Dated.toString().replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));

        var Key = (EventDate.getMonth() + 1).toString() + EventDate.getDate().toString() + EventDate.getFullYear().toString();

        if (C._Events[Key] == null)

            C._Events[Key] = new Array();

        C._Events[Key][C._Events[Key].length] = Events[i];

    }

 

    var NumberOfDays = 0;

    var CustomDayOfWeekEnum = new Array();

    var iWeekDayCounter = 0;

    var DayDiff = 7 - C._StartDayOfWeek;

    var FirstDayOfMonth = (new ExtendedDate(C._Year, C._Month - 1, 1)).getDay();

    var CalRows = new String();

    var CalDays = new String();

    var Heads = new String();

    ..............................

/// Logic continues… please check the Attachments for whole code.

}

The above function renders the calendar to specified container and applies CSS to it. But this is not completed yet, as we still need to bind actions to show hide Events on mouseover and mouseout.

  1. Bind mouseover and mouseevent to Dates to show Event Popup

CalendarHelper.prototype.InitializeEvents = function() {

    $('.date_has_event').each(function() {

        // options

        var distance = 10;

        var time = 250;

        var hideDelay = 500;

 

        var hideDelayTimer = null;

 

        // tracker

        var beingShown = false;

        var shown = false;

 

        var trigger = $(this);

        var popup = $('.events ul', this).css('opacity', 0);

 

        // set the mouseover and mouseout on both element

        $([trigger.get(0), popup.get(0)]).mouseover(function() {

            // stops the hide event if we move from the trigger to the popup element

            if (hideDelayTimer) clearTimeout(hideDelayTimer);

 

            // don't trigger the animation again if we're being shown, or already visible

            if (beingShown || shown) {

                return;

            } else {

                beingShown = true;

 

                // reset position of popup box

                popup.css({

                    bottom: 20,

                    left: -76,

                    display: 'block' // brings the popup back in to view

                })

 

                // (we're using chaining on the popup) now animate it's opacity and position

                        .animate({

                            bottom: '+=' + distance + 'px',

                            opacity: 1

                        }, time, 'swing', function() {

                            // once the animation is complete, set the tracker variables

                            beingShown = false;

                            shown = true;

                        });

            }

        }).mouseout(function() {

            // reset the timer if we get fired again - avoids double animations

            if (hideDelayTimer) clearTimeout(hideDelayTimer);

 

            // store the timer so that it can be cleared in the mouseover if required

            hideDelayTimer = setTimeout(function() {

                hideDelayTimer = null;

                popup.animate({

                    bottom: '-=' + distance + 'px',

                    opacity: 0

                }, time, 'swing', function() {

                    // once the animate is complete, set the tracker variables

                    shown = false;

                    // hide the popup entirely after the effect (opacity alone doesn't do the job)

                    popup.css('display', 'none');

                });

            }, hideDelay);

        });

    });

}

 

The above class can be used in the following way:

 

<div id="DivCalendar"></div>

    <script type="text/javascript">

        var Cal = new CalendarHelper(DayOfWeek.Wednesday, "DivCalendar");

        Cal.GetCalendar(NamesOfMonth.October, 2009);

    </script>

 

The above whole implementation is based on JQuery, Thanks to www.JQuery.org for such a wonderful tool.

CREDITS:

“Alok Arora

Development Team Leader - XentaQSys Technologies

Technical Consultant – Learning Geeks

Join my ASP.NET Knowledge base group by sending "ADD ME!" on gordo_matthews0207@yahoo.ca

Thanks and Regards

 

1 Comment

  • Unfortunately, jQuery plugins (look at JQuery UI and JQGrid) are moving away from object oriented approach...

    They implement functions and properties using magic strings... eg. $('.selector').slider('option', 'animate', true); or .slider( 'disable' )

Comments have been disabled for this content.