Using Prototypes

Using Prototypes

The function type in JavaScript allows you to define custom types. JavaScript doesn’t support static type checking, so you can be creative in creating and using custom types on the fly. The object-oriented programming paradigm has made programming at a higher level the standard for many developers and is more appropriate than procedural programming for most tasks. JavaScript does not support inheritance as other object-oriented languages do, but functions can be used to model data types. As a dynamic language, JavaScript opens the door for some interesting type definitions that can make the code look more familiar to C# and VB.NET developers.

Listing 3-10 (from album.cs) includes a simple definition of an album object in C#. There are string variables for storing the title and artist. There is a constructor that takes the artist and title and stores them in the local variables.

Listing 3-10
Image from book

using System;

public class Album
{
  private string _title = String.Empty;
  private string _artist = String.Empty;

  public Album(string title, string artist)
  {
    this._title = title;
    this._artist = artist;
  }
}
Image from book

You can model the same object in JavaScript using a function. Listing 3-11 (from album.htm) has a JavaScript function with two arguments: title and artist. This is just like the constructor in C#. It takes the two arguments and assigns them to the local variables declared inside the function.

Listing 3-11
Image from book

<script type="text/javascript">
function Album(title, artist) {
 this._title = title;
 this._artist = artist;
}

var album1 = new Album("Lawn Boy", "Phish");
var album2 = new Album("A Picture of Nectar", "Phish");
</script>
Image from book

The album type can be extended to include a property that provides a string representation with both the title and artist. In C#, this would mean adding a new property to the class and recompiling it. In JavaScript, a type can be modified while the script is still running. The prototype property of an object represents the type itself (which shouldn’t be confused with a C language prototype, which is only used to give a function a signature). When using the prototype to extend a type, the change applies to all instances of the object. Listing 3-12 (from album.htm) adds the Listing property to the album type using the prototype.

Listing 3-12
Image from book
<script type="text/javascript">
function Album(title, artist) {
 this._title = title;
 this._artist = artist;
}

var album1 = new Album("Lawn Boy", "Phish");
var album2 = new Album("A Picture of Nectar", "Phish");


function listing() {
 return this._artist + ": " + this._title;
}

Album.prototype.Listing = listing;

alert(album1.Listing());
alert(album2.Listing());
</script>
Image from book

The prototype object represents the object type itself; think of it as a type definition. Any instance of the object inherits all of its prototype information. All of the objects of that type that have already been created get the updated abilities of the base type immediately. Of course, a new function can be added to the object definition so that all instances of the object created in the future will receive the functionality without having to modify the prototype directly. The disadvantage is that each instance gets a copy that way. Using the prototype of an object to extend its functionality is a natural way to model static methods and properties in C# and C++. Modifying the prototype in JavaScript is like modifying the basetype in a .NET type without requiring derived types to be aware of the change.

The prototype object of JavaScript even allows built-in types (such as String) to be modified. When you add functionality to the intrinsic objects provided by the scripting engine, you begin to appreciate the power and flexibility of a dynamic language. You aren’t forced to subclass the type and switch all of your code to use some new derived type to add something that you need. In .NET code, you could do this by re-compiling, when you have access to the original class sources, but in JavaScript you can modify a type without access to the sources. Listing 3-13 (from string.htm) adds the StrangeCaseIt method to the built-in JavaScript String type. Notice that the string is declared and assigned before the new function is created. The StrangeCaseIt function is then added to the String using its prototype. Now all strings, including the one that already exists in the code, have access to this new functionality but haven’t had to pay any overhead associated with carrying the information about the new method along.

Listing 3-13
Image from book

<script type="text/javascript">
var sample = "SomeThing";

function StrangeCaseIt() {
    var returnVal = "";
    for(var i = 0; i < this.length; i++) {
        if(this.substr(i, 1).toUpperCase() == this.substr(i, 1)) {
         returnVal += this.substr(i, 1).toLowerCase();
        }
        else {
            returnVal += this.substr(i, 1).toUpperCase();
        }
   } 
   return returnVal; 
}

String.prototype.StrangeCaseIt = StrangeCaseIt;

var result = sample.StrangeCaseIt();

document.write(sample + "<br />");
document.write(result + "<br />");

</script>
Image from book

The Microsoft AJAX Library makes heavy use of the prototype object to extend functionality and provide a more natural programming environment for the .NET developer. Chapter 7 goes into more detail about this extended functionality and how a familiar object-oriented approach is made available with ASP.NET AJAX. For now, just note that this type system functionality is provided by leveraging the prototype feature of JavaScript.

When using the prototype object, the use of the this reference can be confusing. “this” refers to the object currently in scope at the time the code runs. Remember, the prototype applies to the type itself and all instances of it. It is a specific reference meant to eliminate global members from being included in an object you are extending. When creating object extensions using the prototype, omitting the this reference to use object variables is interpreted as an error. You must specify that the reference is for another variable in the object scope. Think of the prototype extensions as operating in isolation from the rest of the code. You don’t want to be affected by the scope in which the object runs, so you have to be specific about every reference you make. This has to do with the fact that JavaScript uses lexical scoping, not dynamic scoping. Instead of allowing you to reference anything in scope for a prototype extension when the function is called, the object must exist when the function is defined. Always use the this operator to refer to object variables because anything that is not part of the object definition could be different when the code executes next and could lead to problems.