Prototypes


The prototype object was introduced in JavaScript 1.1 as an intrinsic object that simplifies the addition of custom properties and methods to existing objects. When we begin to create objects and prototypes, we are getting into the real nuts and bolts of JavaScript and will be able to create some powerful functionality. An example of how the prototype object works is fairly simple: If it is asked for a method or property that it does not contain, it checks the prototype of the class that created it. It will continue to follow this chain of looking at the parent objects if it does not find the property or method that was called. As a last resort, it will find the Object.prototype (which is the object that creates all objects), but most likely it will call the method in a custom object if it is scoped correctly. The following are three layers that exist in all prototype objects:

  • Object.prototype

  • Class.prototype

  • Instance

These next three layers represent the layers that exist in each of the employee instances:

  • Object.prototype

  • employee.prototype

  • kh

These layers represent object inheritance, which simply means that the kh instance inherits all the properties and methods from the employee and Object objects, and the employee object inherits all the properties and methods from the Object object. This will become more apparent as we learn more about how to use the prototype object and really tackle some complex objects in Part III, "Creating Reusable Components," where we create reusable objects to handle data structuring in Ajax-enabled components.

JavaScript is more powerful than some people give it credit, as you will learn throughout this book. It can be used to create entire dynamic applications, components, and even interact with the server and database as we will see when we implement the server side with Ajax in Part V, "Server-Side Interaction." Prototypes are one of the methods that I chose for creating reusable objects in the samples throughout this book. The other method is a variation on the new operator, which actually creates a pattern that we will be covering in Chapter 14, "Singleton Pattern." There we will actually want to only have one instance of an object. The prototype method for creating objects allows us to easily append properties and methods to objects that we are creating for the first time and extend existing objects to include additional custom functionality. Another reason why this object creation method is so powerful is because we can create multiple instances, making the objects more scalable and reusable. Let's take a look at how these objects are instantiated and how they can be so powerful.

Instances

In order to create instances of a prototype-based object, we must first create a constructor function. The constructor function is the access point to all objects and therefore is the first item that is created in the object. A constructor function can receive unlimited parameters to specify certain attribute or property values. These values are what set one object apart from the next, and is another reason why objects are such a powerful force in programming. Here is an example of a constructor function for an object named employee, as we have created in the previous sections. Again, this object takes three parameters, which specify the unique ID of the employee and his first and last names. The first and last names are simply not enough to set apart one employee from another, especially if two people have the same name; hence, the id parameter.

function employee(_id, _firstName, _lastName) {     // Set properties }


As you can see, creating a constructor function for a prototype object is trivial, yet it is one of the most important pieces of the object.

Creating Properties

Adding properties to our object is extremely simple. In this case, we need to first define parameters in the constructor as we did with the other objects. This allows us to pass values to the properties and set them when an instance of the object is created. After the object is created and the parameters are retrieved by the object's constructor function, we create the properties and set their values. In the following example, we set the id, firstName, and lastName of each employee object that is created.

function employee(_id, _firstName, _lastName) {     this.id = _id;     this.firstName = _firstName;     this.lastName = _lastName; }


As I said earlier, these properties are extremely easy to create and set within an object. The object's properties are scoped to the object by using the this keyword. It is good practice to use this syntax because it is easier to distinguish a local method property from an object property when one is using the this keyword and the other is not.

Thus far, the object is not any different from an object constructor, but this will now change. If we had an instance of an employee object and wanted to add an email address property, we could use the following code:

var kh = new employee(001, "Kris", "Hadlock"); kh.emailAddress = "my@email.com";


This code would not be usable by other instances of the object. For example, if we were to create another employee, she would not have an email address property. In order to add this new property to the employee objectwhich all of the employee instances can see and usewe will need to use the prototype. Here is an example of using the prototype to extend our employee object by adding an email address property.

var kh = new employee(001, "Kris", "Hadlock"); employee.prototype.emailAddress = ""; kh.emailAddress = "my@email.com";


This is not the best solution for creating a new property with the prototype, but it is an example of how we would add this property and have it be accessible to all other instances. Typically, a property would be encapsulated in a method, such as a getter or setter, if not in the constructor. The only reason why we would want to add a property this way is if we needed to extend the object for some reason. The reason why this solution is not the best is because it can become much harder to maintain your code if developers are randomly extending the object in different locations. Keeping the methods and properties centralized in one JavaScript file keeps the management extremely easy, and allows us to reference the file from multiple locations and know that we are receiving the same code from each.

Overriding and Overwriting Properties

We also can override and overwrite properties in a prototype object. For example, we may set a default value for the email address.

var kh = new employee(001, "Kris", "Hadlock"); employee.prototype.emailAddress = "shared@email.com"; kh.emailAddress = "my@email.com"; var jd = new employee(002, "John", "Doe");


Overriding a property value simply changes the value for the instance that you are setting it with. So, now the kh employee has a new email address, but the jd employee has the email address that was set with the prototype.

Overwriting an object property is completely different. If we were to change the email address for the kh employee after we had already set it, we would be overwriting the original value. Here is an example:

var kh = new employee(001, "Kris", "Hadlock"); employee.prototype.emailAddress = ""; kh.emailAddress = "my@email.com"; kh.emailAddress = "new@email.com";


Property Protection

Property protection keeps an object's local properties from being changed by any other instance of that object. In other words, if an instance changed the value of one of its properties, the value would not change in the object from which it is inheriting. For example, let's say that you inherited blond hair from your parents. If your hair color was to darken throughout your childhood, this would not mean that your parents' hair color would change. Here is an example of a value that would change in the instance, not the object.

function employee( ){} employee.prototype.totalVacationDays = 10; var kh = new employee( ); kh.totalVacationDays --; kh.totalVacationDays --; kh.totalVacationDays --;


The reason why this instance is decrementing its own property and not the object's local property is because all the other instances of the object would reflect these changed properties as well. In the example, we are using the total vacation days that an employee has left. Imagine if modifying that property modified every other employee's properties or vacation daysmanagement would have a lot of angry employees on its hands! This is why property protection is so important and the nature of the way these object properties function. Object properties are essential to the makeup of an object, but they can only get us so far. This is why methods are such a powerful addition to object structures. Let's see how we would add methods to our prototype objects.

Methods

Creating a method within a prototype object is a bit different from creating a typical object method. In order to create a method, we need to call the object by name and add the prototype object followed by the method name. Here is an example of the getFullName method that we have been using throughout this chapter using a prototype-based method.

employee.prototype.getFullName = function() {     return this.firstName + " " + this.lastName; }


This method becomes an employee method that can be referenced by every instance that is created from this definition. Each object instance will have its own values for the properties that are being returned; therefore, it will return its custom values if the method is called. For example, if I were to create an employee object and pass it a first name of John and a last name of Doe, calling the getFullName method would return a string representation of John Doe.

Extending Objects with Prototyped Methods

Using prototype objects also enables us to extend an existing object, whether it is an intrinsic JavaScript object or a custom one. Let's take the String object as an example. Say that we would like to add a method to all strings that allow us to turn one into an array of letters by calling a method from that string. In order to accomplish this, we would use a prototype.

function stringToArray() {     var arr = new Array();     for (i=0; i<this.length; i++)     {         arr.push(this[i]);     }     return arr; } String.prototype.convertToArray = stringToArray; var s = "Test String"; document.write(s.convertToArray());


Any string variable that we create in the application that contains this code will now have the capability to intrinsically call the convertToArray method and receive an array of the characters in that string. When you are adding code to native JavaScript objects, you know that you have reached a moment of achievement. Now it is time to create additional functionality that handles common Ajax data manipulation. This is just the beginning of what we can and will accomplish with prototype objects. As I mentioned at the beginning of this chapter, we will be using this object creation method to create custom Ajax-enabled components, which will be reusable in any project, in Part III. These prototype-based objects are extremely flexible and can be scaled to any situation. This is why I have chosen prototypes as the primary method of creating objects for the samples in this book. Utilizing these object creation methods will allow us to create very dynamic and interactive web applications with Ajax.



Ajax for Web Application Developers
Ajax for Web Application Developers
ISBN: 0672329123
EAN: 2147483647
Year: 2007
Pages: 129
Authors: Kris Hadlock

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net