Developing Feather-Weight Webservices with JavaScript

Object orientating and prototypes

We covered getting the argument from the query string and the parseQuery function in Passing JavaScript arguments via the src attribute. While the code is straightforward it could get messy when used in a larger script or if we decide we want it to carry more utility. We want to turn it into an object.

object oriented programming
a way of abstracting complex behavior and data into compact classes or packages. Unlike a regular datum, the object knows its scope/class, its data, and its functions, available methods, and it offers simple ways to interact with it all. Object oriented code is generally inheritable, meaning an object can be reused with modifications by a child class. Built-in reusable extensibility makes it an exceptionally powerful programming idiom.

So far we’ve been developing functional techniques. That is, all our scripts and services have relied directly upon functions. If we write, for example:

function sum () {
  var sum = 0;
  for ( var i = 0; i < arguments.length; i++ ) sum += arguments[i];
  return sum;
}

That’s a function. It isn’t a part of an object or a class (though it does rely tacitly on Number() objects and explicitly on the arguments object). It’s argument is a number list.

Summing a number sequence

var sum_of_num = sum( 1,2,3,5,8,13 );

document.write( sum_of_num );

function sum () {
  var sum = 0;
  for ( var i = 0; i < arguments.length; i++ ) sum += arguments[i];
  return sum;
}

The functional sum

If instead we write:

Array.prototype.sum = function () {
  var sum = 0;
  for ( var i = 0; i < this.length; i++ ) sum += this[i];
  return sum;
}

We’ve written a method, not a function, that any object in the Array() class can call on itself.

Summing a number sequence in an array

var num = new Array( 1,2,3,5,8,13 );

document.write( num.sum() );

The object oriented method sum

You can see they both work fine. You can see they are both easy to understand and roughly the same level of coding difficulty.

Why choose one over the other?

Though it’s rarely used that way in the wild, JavaScript is an object oriented language which supports inheritance and custom classes. Purists will argue what this really means but all the data types in JavaScript are objects as well as the DOM, obviously. A few naked global function calls like escape() aside, it’s all objects. Even escape() and friends are transparent methods of the contextual Global() class.

We’re going to go object oriented because we need our code to be reusable and because we want get the complexity out of the way. Yes, the complexity still exists, but with OO it’s beneath a nice API, the hackers’ version of the UI. It’s not a tangle to reuse or a stumbling block to expanding functionality.

This isn’t necessarily the right™ way to do it. It is natural, though. JavaScript is an object oriented language already. And there are compelling reasons to move that way.

Imagine, for example, if you had 30 functions in a script to do date work instead of a Date() object that had those methods built-in. And you had to copy those functions—weighing in at about 1,000 or more lines of code—into every script where you wanted to use them. Just say no.

How to write an object class

Basically any function in JavaScript can return an object if it is used with the new keyword in front of it. It really could not be easier. Not all functions would return a sensible object, of course. They need to be written to that end.

When you do call a function, now an object constructor, with new, like new Date(), it gets a special property: this. this is a built-in keyword for the object that’s created.

A simple object class/constructor

function Guitar ( model, make, price ) {
  this.model = model || '?';
  this.make  = make  || '?';
  this.price = price || '?';
}

It doesn’t get much simpler or elegant than that in any programming language.

How the class is used

var righteousAxe = new Guitar( "'59 Les Paul", "Gibson" );
document.write( 'I love this ' + righteousAxe.model + '!' );

The output

The capitalized “Guitar” is a style convention for constructors in JavaScript but has no programmatic meaning. It could be called royalFizbin() or ___URKLE_1_2_3_(). We choose the shortest sensible name and stick with convention so we don’t confuse other developers, or ourselves when we return to the code a year later and wonder how we got anything done being that drunk all the time.

Method, man

You can already see how easy it is to wrap up data in an object. We can have as many attributes as we want and they can be added on the fly, just like with regular Object()s. The next step to the power of classes is built-in functions the objects can call on themselves: methods.

A function installed into an object, via its prototype property, is a method. It has no name outside the object. It is only known to denizens of the class it’s installed in.

Methods: setPrice() & showPrice()

Guitar.prototype.setPrice = function ( price ) {
  this.price = price;
}
Guitar.prototype.showPrice = function () {
  document.write( this.price );
}

setPrice() & showPrice() in action

var righteousAxe = new Guitar( "'59 Les Paul", "Gibson" );
document.write( 'I love this ' + righteousAxe.model + '!' );

righteousAxe.setPrice('$62,000');

righteousAxe.showPrice(); // Oh, well... There's always Epiphone.

Method() != Function()

Remember that a method belongs to a class; to any object created through that class. It’s not the same thing as a method. If we do this now:

Wrong-o was his name-o

var sadTruth = new Guitar( "Sears catalog special", "Cort" );
document.write( 'I can afford this ' + sadTruth.model + '.<br />' );
sadTruth.setPrice('$119');
document.write( "It's only " + showPrice() );

We’ll get a fatal error. There is no such function as showPrice(). It’s a method that can only be invoked through Guitar() objects.

Class data

One more thing that will come handy often. Each object created with new Whatever() is often referred to as an instance of the class. The class itself can also have attributes which are available within the class to all instances.

Guitar.stringCount = 6;
Guitar.tuning = [ 'E','A','D','G','B','E' ];

Then stringCount and tuning1 can be used throughout the Guitar class and maintain themselves, their state, across different objects, or instances. Class data isn’t visible without the Guitar prefix so you can’t have namespace collisions with it; though it can be overridden or overwritten by another class extension which is easy to accomplish but difficult to do accidentally.

Don’t start from scratch if you can help it

The best part about classes is that they can be inherited and extended via the prototype property of all objects. We don’t even need to make our own. We can built on others’ or on the built-ins. For example, we can extend Array() objects to be able to join themselves into a string with a serial comma.

Serial comma join for Array()

Array.prototype.serial = function () {
  switch( this.length ) {
    case 0 : return false;
    case 1 : return this[0].toString();
    case 2 : return this.join(' and ');
    default: return this.slice(0,-1).join(', ') +
               ', and ' + this[ this.length - 1 ];
  }
}

Array.serial() test script

var things = new Array( 'Caracal', 'Margay', 'Serval', 'Ocelot' );

for ( var i = 1; i <= 5; i++ ) {
  document.writeln( things.serial() + ".<br />" );
  things.shift(); // extinction...
}

The rare cats you’ll have a chance to see in your lifetime

Where to start for our services

We are certain we like the ability to get query string arguments along with our client scripts. So that’s an excellent place to begin. The code that does it isn’t so simple that we’d want it pasted around everywhere drifting apart and adapting to its various niches. Our first class will build an Args() object that has automatic (Greek for self) access to the parsed query string parameters.

When we’re done we’ll be able to use something like this in a client call:

<script
 src="http://elektrum.org/sandwich.js?cheese=brie;bread=honey+wheat"
 type="text/javascript"> </script>

And get at it on the service side as easily as this:

var args = new Args();
document.write("Cheese: " + args.cheese);
document.write("<br />");
document.write("Bread: " + args.bread);

Once it’s out in its own class we’ll be able to use new Args() objects to our heart’s delight in as many different services as our fevered brains can cook up. Here we go: Args().

1 We know there are another couple hundred string counts and tunings. We also know that a Cort set up correctly can be an excellent guitar.
« greeking.js in action · Args() »
Google
 
Web Developing Featherweight Web Services with JavaScript
This is version 0.57b of this manual. It is a beta version with some gaps. We are grateful for feedback.

The code is the manual has not yet been fully tested against Internet Explorer. Bug reports are welcome.
An Elektrum Press Online