JavaScript: Object Orientated Programming

JavaScript is an object-orientated language, some even use the term prototype-orientated language.

In this post I am going to have a quick look at OOP within the JavaScript realm, and have a look at prototyping.

In languages like C# & PHP, we create objects from classes/structs etc, within JavaScript however, we need to use functions to define our objects, observe:

 
/*constructor*/
function base(name)
{
	/*private*/ var title = 'base object:';
	/*public*/this.name = name;
	/*global*/value = 10;
 
	this.display = /*public*/function(message)
	{
		alert(title + message);
	}
 
	this.toString = /*public*/function()
	{
		return "base";
	}
}
 
var a = new base('test1'); // instantiated object
a.display(a); // base object:base
alert(a.name); // base object:
alert(a.title); // undefined
alert(value); // 10
 

Notice my comments, I gave certain functions fictitious access modifiers (eg private/public) to give you an idea of scope visibility of these members.

The title property can only be accessed within the instantiated object (like a private member), the this.name field is publically available using the object. Notice the value field, if we omit the var keyword, it becomes a global variable - one can access it anywhere within the scope of our page.

Constructor? Well, the whole function essentially represents our constructor.

Another term used in OOP is "inheritance", which relates to the fact that we can add certain methods/properties etc from another class (function in this case), to our class(function) in question. In JavaScript this is achieved using prototypes, observe:
 
/*constructor*/
function derived()
{		
	/*private*/function getDate()
	{
		return new Date();
	}
 
	this.showdate = /*public*/function()
	{
		this.display(getDate());
	}
}
 
derived.prototype = /*inherit*/new base('test2');
derived.prototype.toString = /*overrides*/function()
{
	return "derived";
}
 
var b = new derived();
b.showdate();
b.display(b);
alert(b.name);
 

Inheritance is achieved by assigning an instance of the function we wish to "inherit" from, to our prototype - which copies the members within that instance to our derived object.

It is even possible to override inherited members, using prototypes, eg. the toString method.

Prototypes also allow us to add functionality to existing built-in objects, similar to extension methods, in the following snippet we "alias" the addEventListener method in Mozilla to the attachEvent method - by "extending" the built-in Object.
 
if (!window.attachEvent) {
	Object.prototype.attachEvent = function(event, handler)	{
		this.addEventListener(event.substring(2), handler, false);					
	}
}
 


Note : Have a look at the "private" getDate function within our derived function, if you attempt to access the instance you're working with (using the this keyword), you will notice that you actually get an instance of the current window object and not the actual instance it "lives" in - rather dodgy? Which means getDate isn't really a private method in the true sense of the word.

Its also possible to create objects using javascript object notation (JSON), these objects unfortunately lack constructors, meaning we can't create/reuse new instances, since we're actually defining an inline object.
 
var a = {
	display : function(message)
	{
		alert(message);
	},
	toString : function()
	{
		return 'a';
	}
};
 


Unless you're planning to return the object from a "constructor" function...(mmmm???)
 
function base()
{
	var a =
	{
		// Object definition
	};
	return a;
}
 


Finally I noticed on a few sites people asking about creating static methods, we can achieve something similar by doing the following:
 
derived.somefunction = /*static*/function()
{
	alert('somefunction called');
}
derived.somefunction();
 

Post/View comments
 

Sorting: ASP.net (C#) and SQL 2005/8

Source code for the following post can be downloaded here.

The following post is a bit of a follow up post on the post I did a while ago about paging.

Equally to paging, sorting is also one of those common tasks when working with data.

Similarly to the paging post we create a table, except we're adding a datetime field in this example, which tells us when an user was added to our "system".

 
CREATE TABLE [dbo].[friends](
	[id] [int] IDENTITY(1,1) NOT NULL,
	[firstname] [varchar](50) NOT NULL,
	[lastname] [varchar](50) NOT NULL,
	[created] [datetime] NOT NULL,
 CONSTRAINT [PK_friends] PRIMARY KEY CLUSTERED 
(
	[id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
 


The default value for the create date:
 
ALTER TABLE [dbo].[friends] ADD  CONSTRAINT [DF_friends_created]  DEFAULT (getdate()) FOR [created]
 


You'll also need to create some indexes for the fields you're planning to do sorting on, to avoid table scans:

 
CREATE NONCLUSTERED INDEX [ix_friends] ON [dbo].[friends] 
(
	[firstname] ASC,
	[lastname] ASC,
	[created] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
 
 


Add some data:
 
INSERT INTO friends(firstname, lastname, created) VALUES ('Julie', 'Truter', getdate() - 10)
INSERT INTO friends(firstname, lastname, created) VALUES ('Eugene', 'Stander', getdate() - 50)
INSERT INTO friends(firstname, lastname, created) VALUES ('Pam', 'Nizar', getdate() - 120)
INSERT INTO friends(firstname, lastname, created) VALUES ('Alexander', 'Mehlhorn', getdate() - 4)
INSERT INTO friends(firstname, lastname, created) VALUES ('Roland', 'Cooper', getdate() - 10)
INSERT INTO friends(firstname, lastname, created) VALUES ('Loren', 'Stevens', getdate() -2)
INSERT INTO friends(firstname, lastname, created) VALUES ('Edward', 'Anderson', getdate() - 5)
INSERT INTO friends(firstname, lastname, created) VALUES ('Wayne', 'Kleynhans', getdate() - 13)
 
 


The stored procedure we're going to use to assist us in sorting, will look something like this:
 
CREATE PROCEDURE [dbo].[sortFriends]
 	@sort AS VARCHAR(20)
AS
BEGIN
	SELECT id, firstname, lastname, created
	FROM friends
	ORDER BY
	CASE @sort
		WHEN 'firstname' THEN firstname
		WHEN 'lastname' THEN lastname
		ELSE NULL
	END ASC,
	CASE @sort
		WHEN 'created' THEN created
		ELSE NULL
	END ASC,
	CASE @sort
		WHEN 'firstname DESC' THEN firstname
		WHEN 'lastname DESC' THEN lastname
		ELSE NULL
	END DESC,
	CASE @sort
		WHEN 'created DESC' THEN created
		ELSE NULL
	END DESC
END
 


Notice the case statements in the order by clause, I seperated the created field from the other (firstname, lastname) fields - if we process these fields in the same case statement, we'll need to convert the datetime field to a varchar - which I am avoiding for performance reasons.

Also notice the "ELSE NULL", SQL will ignore these order conditions, if our case condition isn't met.

Moving on to the ASP.net side of things..

In the following snippet we create a gridview control, notice the sortexpression attributes, those are the expressions we're going to send to our stored procedure. Note: the control will automatically concatenate our descending command if needed, eg firstname DESC.
 
<asp:GridView ID="gvFriends" runat="server" DataSourceID="obsFriends" AllowSorting="true"
	AutoGenerateColumns="false" OnRowCreated="gvFriends_RowCreated">
	<Columns>
		<asp:BoundField DataField="firstname" HeaderText="Firstname" SortExpression="firstname" />
		<asp:BoundField DataField="lastname" HeaderText="Lastname" SortExpression="lastname" />
		<asp:BoundField DataField="created" HeaderText="Created" SortExpression="created"
			DataFormatString="{0:d}" />
	</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="obsFriends" runat="server" SelectMethod="sortFriends" TypeName="friends"
	SortParameterName="sort"></asp:ObjectDataSource>
 
 


This is what our select method is going to look like:
 
 
[DataObject()]
public class friends
{
    [DataObjectMethod(DataObjectMethodType.Select)]
    public static DataTable sortFriends(String sort)
    {
        using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Default"].ConnectionString))
        {
            using (SqlCommand command = new SqlCommand("sortFriends", connection))
            {
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddWithValue("@sort", sort);
                using (SqlDataAdapter adapter = new SqlDataAdapter(command))
                {
                    DataTable dt = new DataTable();
                    adapter.Fill(dt);
                    return dt;
                }
            }
        }
    }
}
 
 


When an user clicks on a field in the header of the gridview, it will send the appropriate command to the stored procedure and sort our results.

Unfortunately M$ didn't provide functionality which displays which field and which direction (descending/ascending) are currently being sorted.

Luckily this is functionality we can add ourselves, we attach the following code to the rowcreated event of the gridview.
 
if (e.Row.RowType == DataControlRowType.Header)
{
    List<LinkButton> linkButtons = (from headerCells in e.Row.Cells.OfType<DataControlFieldHeaderCell>()
                                    let linkButton = (headerCells.HasControls()) ? headerCells.Controls[0] as LinkButton
                                                                                : null
                                    where linkButton != null
                                        && ((GridView)sender).SortExpression == linkButton.CommandArgument
                                    select linkButton).ToList<LinkButton>();
 
    foreach (LinkButton linkButton in linkButtons)
    {
        Image image = new Image();
        image.ImageUrl = String.Concat("~/images/",
                                (((GridView)sender).SortDirection == SortDirection.Ascending ? "asc" : "desc"),
                                ".gif");
        linkButton.Controls.Add(new LiteralControl(String.Concat(linkButton.Text, " ")));
        linkButton.Controls.Add(image);
    }
}
 


You will notice I wrote a LINQ query to retrieve the linkbutton(s) currently "responsible" for sorting - alternatively one can simply retrieve them using a foreach statement, but I felt lucky ;)

Once we find them, we add the proper picture to these controls, which gives us a visible representation of which columns are currently being sorted.

Post/View comments
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25