Chapter 4

JavaScript Objects


CONTENTS

The idea of object-oriented programming is not a new one. It actually dates back over 30 years, and has gone through several phases of popularity in that time. Currently, object-oriented programming is considered by many to be an established concept that should be part of all modern programming languages. There are several different conflicting definitions of object-oriented programming. Fortunately, there are some key concepts that are shared by (almost) all versions of objected-oriented programming.

At its most basic level, object-oriented programming is a style of programming in which related concepts are grouped together. If you have five data elements and three functions that manipulate those elements, then you group those elements and functions together into a generic container known as an object. This is the common ground shared by (almost) all object-oriented programming languages. Differences arise in the details of how such containers are organized, and in how their contents can be accessed and modified.

An analogy can be made between home ownership and object-oriented programming. Everyone's house has a kitchen, some bedrooms and bathrooms, stairs, flooring, and so forth. Some homes have spiral staircases, Art Deco ironwork, and a gazebo in the back yard. Others have a completely utilitarian layout based on a linear architecture with not a rounded corner in sight. When talking about your home, you describe both the basic aspects ("yes, of course we have a basement") and also the embellishments ("the basement has a painfully hard cement floor"). When talking about what an object means in JavaScript, it's necessary to also talk of two levels. The basic aspects of the way JavaScript handles objects is known as its object model. The embellishments constitute the extensive set of features of the predefined objects in JavaScript, as well as those aspects of the language that can be used to create and use new, user-defined objects.

Objects, Properties, and Methods in JavaScript

Before we can delve into object-oriented programming in JavaScript, it is first necessary to review some of the basic concepts of object-oriented programming itself. You have already had a brief introduction in the "Functions and Objects" section of chapter 2, "JavaScript: The Language." This section takes you further, and explains several critical and often misunderstood ideas.

Object-Oriented Programming Concepts

We already know that an object is basically a container for related items. Rather than carry around money and credit cards in many different pockets and folders, many people choose a more unified method: they keep their money in a wallet. Perhaps they even keep their change in a change purse. The wallet is a container for related items. This is not to say that all such items must be in that wallet; this is often a near-impossible goal for even the most organized individuals. As a flexible principle, however, it is of enormous utility.

Objects operate the same way. Objects collect related data items in a single place and make it simpler, or at least more logical, to access those items. As we have already seen, JavaScript refers to the items collected within an object as its properties. You may also recall that JavaScript objects not only store data, they also store functions. It is useful to keep functions that manipulate data items in a specific way with those data items themselves. These functions are known as the methods of an object.

The JavaScript Date object is a perfect example of the benefits of this kind of organization. As the name implies, a JavaScript Date object is used to store a date, and also a time. The Date object also has a very particular set of methods that are useful in converting string representations of dates in Date objects. While these functions are vitally important when manipulating strings such as "Nov 23, 1990," they do not really have sweeping application elsewhere. In a word, they are date-specific. It makes good sense to keep these methods with Date objects, rather than making them generally available functions.

In addition to the concepts of object, property, and method there is a fourth, somewhat more subtle, concept that is also of great importance: the instance. The relationship between an object and an instance of an object is the same as the relationship between a data type and a variable of that data type. In the typeless language such as JavaScript, this distinction is blurred but is still present. Another way to think of this distinction is to think of an object as a set of shelves, some of which may be occupied while others are not. You convert that object into an instance when you completely fill in all the empty shelves.

While the object Date is an abstract thing that does refer to any specific date, an instance of the Date object must refer to some specific date. Its empty slots, which specify the actual day, month, year, and so forth, have all been assigned specific values.

Defining Your Own Objects: The new Statement

Now that we have presented the basic object foundation upon which JavaScript rests, it is time to consider how these concepts are implemented. How does one create objects and instances in JavaScript? In fact, you already know part of the answer to this question, as objects are created by defining a very special sort of function.

Let's pursue the home ownership analogy even further and define a house object. The fundamental properties of our house object will be as follows:

To define an object to hold this information, we write the function shown in listing 4.1. Note that this function makes use of the extremely important keyword this, which always refers to the current object. In this case it refers to the current object we are creating.


Listing 4.1  Defining a Function to Create a house Object

function house( rms, stl, yr, garp ) {          // define a house object
     this.rooms = rms;                    // number of rooms (integer)
     this.style = stl;            // style, e.g. Colonial, Tudor, Ranch (string)
     this.yearbuilt = yr;                    // year built, integer
     this.hasgarage = garp;                    // has a garage? (boolean)
     }

There are several things to notice about this object definition. First of all, the name of the function is the name of the object: house. Second, this function does not return anything. When functions were first introduced in chapter 2, it might have seemed mysterious how a function could actually do useful work without a return statement, since everything inside a function is local. Using a function to create an object works by modifying this, so that it need not return anything. You can also have the function return(this). Using this explicit return statement has the same effect as the code shown in listing 4.1.

This example shows how a house object is defined. It does not create a specific house instance. The house object has four slots to hold the four properties rooms, style, yearbuilt, and hasgarage. A specific house instance will fill those slots with actual values. Instances are created using the new statement combined with a function call. The keyword new is required, since it tells JavaScript that we are creating an instance rather than just calling a function. We could create an instance of house, named myhouse, as follows:

var myhouse = new house( 10, "Colonial", 1989, true );

Note that the instance myhouse is treated just like any other variable. It must be declared using var. Now that myhouse has been created we can refer to its properties using the dot operator (.). myhouse.rooms has the value 10, myhouse.style is the string "Colonial," myhouse.yearbuilt is 1989, and myhouse.hasgarage is the boolean value true. The fact that rooms and yearbuilt are integers, style is a string, and hasgarage is a boolean is only implicit, of course. There is nothing stopping us from creating a house instance in which the hasgarage property has the string value "yes" rather than a boolean value. Care must be taken to avoid this kind of type confusion.

NOTE
Object properties are typeless, just like all other variables in JavaScript. The new operator does not protect you against inadvertently assigning an inappropriate value to a property.

Objects as Arrays

Many programming languages support array data types. An array is an indexed collection of items all of which have the same underlying type. In C or Java, for example, we can say int iarr[10]; which defines a collection of 10 integers. These integers are referred to as iarr[0] through iarr[9]. These two languages use zero-based indexing, which means that the first element of the array is at location 0 and the last element of the array is at one less than the length of the array-9 in this case. Other languages have one-based indexing, in which the elements range from 1 up to the length of the array. This might seem more intuitive, but zero-based indexing is actually the more common form.

JavaScript also has arrays that use zero-based indexing. In JavaScript, however, arrays and objects are really two views on the same concept. Every object is an array of its property values, and every array is also an object. Our myhouse instance, for example, is an array with the following four elements:

myhouse[0] = 10;               // rooms
myhouse[1] = "Colonial";     // style
myhouse[2] = 1989;               // yearbuilt
myhouse[3] = true;               // hasgarage

There might not seem to be a lot of advantage to referring to objects in this more numeric and less informative manner. You have to remember which index corresponds to which property. However, this alternate form of access makes it possible to access the properties sequentially, rather than by name, which is sometimes very useful. If we know that house objects always have four members then we can write the function shown in listing 4.2 to display the property values.


Listing 4.2  A Function That Displays the Properties of a house

function showhouse( somehouse ) {               // display properties of  a house instance
     for( var iter = 0; iter < 4; iter++) {     // four properties exactly
          document.write("<BR>Property " + iter + " is " + somehouse[iter]);
     }
     document.write("<BR>");
}

If we call this function as showhouse( myhouse ) the four properties of the myhouse instance are displayed. This function must be called with an instance, not an object. It would be an error to try showhouse( house ). Since there are several alternative ways of writing it, we will revisit this function when we have learned more about methods and the for…in statement.

One deficiency of the showhouse function should strike you immediately. It relies on the implicit knowledge that every house instance has exactly four properties. If we were to augment the definition of a house object by adding a property known as taxrate (a floating-point number describing the current real estate taxation rate on the house), then the showhouse function would need to be modified to increase the loop count in the for statement from 4 to 5. If we neglect to do so then the showhouse function would only print the first four properties, and would never print the taxrate.

An even more disastrous error would occur if we defined the house object to have only three properties, but forgot to drop the loop count to 3; then the reference to somehouse[3] would refer to a nonexistent array member. This type of error is known as an out of bounds error, since it refers to an array element that was not within the boundaries of the array. There is a very simple way to avoid this problem and write the showhouse function in a more general manner.

TIP
Define all objects with a length property, which gives the number of properties in the object. Make the length property the first property.

Using the preceding tip, we can rewrite the definition of the house object to include a length property as the first property, and then generalize the showhouse function to be completely independent of any prior knowledge of the house object. This code for the new house object and showhouse function is shown in listing 4.3. This code can be found in the file house1.js on the CD-ROM.


Listing 4.3  house1.js  A Better house Object That Knows Its Own Length

/*
  This function creates a house instance whose first property,
  at array index 0, contains the number of properties in the house
  instance.
*/

function house ( rms, stl, yr, garp ) {
     this.length = 5;          // four informative properties, and  length
     this.rooms = rms;          // rooms
     this.style = stl;          // architecture style
     this.yearbuilt = yr;          // year constructed
     this.hasgarge = garp;          // does it have a garage?
}

/*
  This function displays a house instance using its length property
  to determine how many other properties to display
*/

function showhouse( somehouse ) {               // display properties of  a house instance
     var nprops = somehouse.length;          // number of properties
     for( var iter = 1; iter < nprops; iter++) {//iterate over all properties except length
          document.write("<BR>Property " + iter + " is " +  somehouse[iter]);
     }
     document.write("<BR>");
}

This house object function takes four parameters, as before. It sets its length property to this number plus 1, since there are four meaningful properties (rooms, style, yearbuilt, and hasgarage) and the length property itself. Each of the meaningful properties have moved up 1, so that if we say myhouse = new house( 10, "Colonial", 1989, true) the array representation of myhouse becomes

myhouse[0] = 5;                 // total # of properties
myhouse[1] = 10;               // rooms
myhouse[2] = "Colonial";     // style
myhouse[3] = 1989;               // yearbuilt
myhouse[4] = true;               // hasgarage

The showhouse function starts by looking at the length property and uses that to set the termination condition for the for loop. The constant 4 of listing 4.2 has been replaced by the variable nprops which holds the length of the myhouse array. This version of showhouse only prints the properties of interest; it does not print the length property. This is why the for loop begins at 1 rather than at 0. The property myhouse[0] is the length property.

This use of the length property is a typical example of the true nature of object-oriented programming. One of the fundamental ideas in object-oriented programming is the idea of encapsulation, which is a long-winded way of saying keeping related things in the same place. In the previous definitions of house and showhouse (see listings 4.1 and 4.2), the length of the house object was present in two places. It was implicitly present in the definition of house itself, and it was also present explicitly, as the upper limit in the for loop. The doctrine of encapsulation says that this is bad. The length of an object should only be stored in one place-in the object itself. By the same token it might be argued that the showhouse function should really be part of the house object, too. The "Method Functions and this" section later in this chapter describes how to do this.

Despite the power of this technique, it might still seem less than obvious to refer to properties by index rather than by property name. JavaScript provides a third technique, which is a hybrid of the dot style (.) and the array style ([]). Object properties may be referred to not only as indexed array elements but also as named array elements. This type of array is known as an associative array. The set of properties of the myhouse instance could also be listed as

myhouse["length"] = 5;
myhouse["rooms"] = 10;
myhouse["style"] = "Colonial";
myhouse["yearbuilt"] = 1989;
myhouse["hasgarage"] = true;

CAUTION
JavaScript arrays can be accessed by integer index or by property names. Property names are case-sensitive. Integer indices are limited by the length of the array. If you refer to non-existent array elements, by name or by index, it either generates a JavaScript error or gives you an invalid value.

Using Variable Length Arrays and Extended Instances

There is one final point to be made about the difference between house object and its various instances. Suppose we create another instance of house, named yourhouse, using the following call to new:

yourhouse = new house( 26, "Tudor", 1922, true );

myhouse and yourhouse are both instances of the house object. Both result from filling in the four slots in the house template with four specific pieces of information that define myhouse and yourhouse (as well as the fifth, hidden piece of information, the length). It is possible to dynamically extend an instance by simply tacking on a new property. If you feel the need to also record the fact that your house has two tool sheds and a gazebo you can write

yourhouse.sheds = 2;
yourhouse.hasgazebo = true;

These two statements add two new properties to the end of the yourhouse array. The sheds (integer) property is yourhouse[5] and the hasgazebo (boolean) property is yourhouse[6]. Dynamic extensions only apply to specific instances. The myhouse instance is not affected, nor is the house object changed in any way. If we execute showhouse( myhouse ) it prints out exactly the same as it did before. If we create a third house named pizza

pizza = new house( 3, "Restaurant", 1993, false ); 

it will not have either a sheds property or a hasgazebo property. Figure 4.1 illustrates the relationship between the house object and its various instances.

Figure 4.1 : Instances inherit their structure from the underlying object, but can also be extended.

NOTE
Dynamic extensions are completely local to a particular instance. The underlying object and all other instances-past, present, and future-are not affected. 

There are some situations in which dynamic extensions are absolutely essential, and dramatically simplify programming. For the most part, however, dynamic extensions should be used with great care, as they can be the source of numerous errors. In fact, we have already made one such error, which shows itself if we attempt to execute the function showhouse( yourhouse ). Since the length element of the yourhouse instance has not been modified, it still has the value 5, so that only array elements 1 through 4 (properties "name" through "hasgarage") are displayed. The two new properties will not be displayed. When we added sheds and hasgazebo, we should have also said

yourhouse.length += 2;

to account for the two new properties in this instance. This is precisely the type of error that is easy to make. In general, it would be much better for the house object to always have sheds and hasgazebo properties, which are seldom used, than to randomly glue them on. The most efficient way to do this is discussed later in the "Functions with a Variable Number of Arguments" section of this chapter.

The one common case where dynamic extension is extremely useful is in variable length arrays. Since object properties are just array elements, and since these elements can be referred to using a numerical index, it is easy to write an object creation function that creates an array of arbitrary size and content. The function in listing 4.4 can be used to define an object that is an array of strings. The number of strings in the array is the first argument, and the initial value for each element is the second argument.


Listing 4.4  A Variable Length Array-Of-Strings Object

function stringarr( howmany, initstr) {     // "howmany" strings
     this.length = howmany;
     for( var i = 1; i <= howmany; i++ ) {
          this[i] = initstr;                // initial value "initstr"
     }
}

If we call this function as

mystringarr = new stringarr( 100, "spoon" );

it creates an instance with 101 properties. The first, at index 0, is the all-important length property. The next 100, at indices 1 through 100 inclusive, are initialized to the string "spoon." Presumably at some point in the future these 100 strings will be set to some other, less uniform values. It is important to initialize all the properties values to something (the empty string "" would do in this case).

If we later find that we need more than 100 strings we do not need to create a new, even longer, stringarr instance. Instead we can dynamically extend the array to include these new strings. It is essential that the length property be updated in this case, as there is no other way of determining how many elements are in the array, short of counting them with a for...in loop (see the following section). The following statements add three new strings and update the length:

mystringarr[101] = "I'm";
mystringarr[102] = "doing";
mystringarr[103] = "laundry";
mystringarr.length += 3;

The for…in Statement

Chapter 2introduced the extremely useful for statement. The standard form of the for statement begins with a clause that defines the initial state of the for loop, the condition under which it will terminate, and the way in which it is updated at the end of each iteration. There is also a variant of the for statement which may be used to iterate over the properties of an object. This statement, the for...in statement, has the following form:

for ( varname in objname ) {
     forbody
     }

In the for...in statement varname is the name of a variable that takes on the successive property names of the object objname. This form of the for statement also permits the varname to contain a var declaration. Using the for...in statement we can write yet another form of the showhouse function, which does not rely on the presence of a length property. This function is shown in listing 4.5. This version actually works on any instance or object, not just on instances of house, so it has been renamed showany. This function can be found in the file showany.js on the CD-ROM.


Listing 4.5  showany.js  A Function That Displays the Properties of Any Object

function showany(anyobj) {        // display properties of an instance   or object
     for( var iter in anyobj ){     // iterate over all properties
          document.write("<BR>Property " + iter + " is " +  anyobj[iter]);
     }
     document.write("<BR>");
}

Method Functions and this

One of the most powerful aspects of object-oriented programming in JavaScript is the ability to create objects with functional properties. You may recall that these functional properties are known as methods. Aside from being a convenient organizational principle, there are other distinct advantages to associating functions with objects. We have already seen the special keyword this which is used in object creation. It's also used in method functions to refer to the current object. To see how this works, consider one more variation on the house object and the showhouse function shown in listing 4.6 (the file house2.js on the CD-ROM).


Listing 4.6  house2.js  The showhouse Function as a Method of house

/*
  This function creates a house instance with a "show" method
*/
function house( rms, stl, yr, garp ) {
     this.length = 5;               // four info props and length itself
     this.rooms = rm;               // rooms; prop [1]
     this.style = stl;               // style; prop [2]
     this.yearbuilt = yr;               // year built; prop [3]
     this.hasgarage = garp;               // garage?; prop [4]
     this.show = mshowhouse;          // the showhouse method; prop [5]
}
/*
  This function is the show method of the house object
*/
function mshowhouse() {          // note: no arguments!
     var nprops = this.length;  // len of property array not including show
     for ( var iter = 1; iter < nprops; iter++) { //iterate
          document.write("<BR>Property " + iter + " is " + this[iter]);
     }
     document.write("<BR>");
}

This version of the instance creation function house not only has the usual four pieces of house information (rooms, style, yearbuilt, and hasgarage) and the length property, which gives the number of properties, it also has a final property named show, which is set equal to the function mshowhouse (it has been given a new name to emphasize that it is now a method function). Note that we did not count this method in the length of the property array (although we could have).

The method version of the showhouse function is shown next. It does not have any arguments. Instead, it refers to its enclosing object as this. The usual for loop works as before. Since we have deliberately shortened the length property by one, only the properties with indices 1 through 4 are displayed. We have used both a dot style (.) reference and an array style ([]) reference with this, which acts just like any normal instance. If we execute the show method on the myhouse object, a display something like figure 4.2 appears.

Figure 4.2 : Method functions can be used to display the properties of their instances.

Since this function takes no arguments, you might wonder how it is used. The answer is that since the show method is a property just like any other property it may be accessed in the same way. The statements

myhouse.show();
yourhouse.show();
pizza.show();

all work exactly the same as their nonmethod counterparts

showhouse( myhouse );
showhouse( yourhouse );
showhouse( pizza );

This particular method function took no arguments, and was also void; it does not return any value. Method functions can take as many arguments as you wish, and can also return values. Listing 4.7 shows a very simple method function that takes the current year and an argument, and returns the age of the house as its value. It checks the argument for validity and returns -1 if the current year is actually earlier than the yearbuilt property.


Listing 4.7  A Method Function for Displaying the Age of a House

function howold ( curyear ) {                // current year passed as arg
     if ( curyear < this.yearbuilt )      // invalid year: too early
          return(-1);               // no time travel (yet)
     return( curyear - this.yearbuilt );     // return difference
}

This method must be added to the object defining function house in order for it to work, of course. This function would be called by a standard property reference such as

myhouseage = myhouse.howold( 1996 );

This type of function call is no different than a standard function call such as showhouse( myhouse ). The only difference between method functions and other functions is that method functions may use this as an indirect way of naming the object that contains them.

TIP
If you have special purpose functions that only operate on instances of an object, then those functions should be methods of that object.

Nested Objects

Object properties are typeless quantities. They may be ordinary variables of any implicit type. Our house object contains properties that are implicitly integers, strings, and booleans. It also contains functional members (methods). In a very real sense, every new object is a new data type, and every instance of that object is a new variable with its object as the underlying, implicit type of that instance. Since JavaScript is typeless, does this mean that objects can contain other objects? In a word, yes.

Suppose we create a new object called desc that holds some common pieces of information about various items. In particular, the desc object has properties for length, width, height, and color, and a method for computing the volume. The definition of this object and its volume method are shown in listing 4.8. This code can be found in the file descob.js on the CD-ROM.


Listing 4.8  descob.js  A Description Object and Its Volume Method

/*
  The object creation function. The len, width and height
  properties will be specified in meters. The color will be
  a string.
*/
function desc( ln, wd, ht, col) {     // describe something
     this.length = 5;          // four properties and length of the 
                                             array
     this.len = ln;          // length of the thing; prop [1]
     this.width = wd;          // width of the thing; prop [2]
     this.height = ht;          // height of the thing; prop [3]
     this.color = col;          // color; prop [4]
     this.findvolume = findvolume;     // volume computation method
     }
/*
  The volume computation method. If the ismetric argument is
  true then the metric volume will be returned; otherwise
  the volume in cubic feet will be returned
*/
function findvolume ( ismetric ) {
     var mylen, mywid, myht;
     var conv = ( 39.37 / 12.0 );     // conversion from metric to  English
     if ( ismetric == true ) {
          mylen = this.len;          // metric by default
          mywid = this.width;          // ditto
          myht = this.height;          // ditto
     } else {
          mylen = this.len * conv;      // convert
          mywid = this.width * conv;
          myht = this.height * conv;
     }
     return( mylen * mywid * myht );     // return volume
}

We can now add a desc object as a property of the house object. We could simply add length, width, height, and color properties directly to the definition of house, but this would go against another fundamental principle of object-oriented programming: object reuse. The desc object is very general. It can be used to describe a house, a car, a boat, or a tea cozy. It makes good sense to encapsulate these common properties in the desc object and then reuse that object's definition over and over by including it with the house, car, boat, and tea cozy objects. It would be serviceable, but wasteful, to repeat the same information in all these object definitions. Listing 4.9 (part of the CD-ROM file house3.js) shows the latest version of house object creation function.


Listing 4.9  house3.js  The house Object with a desc Sub-Object

/*
  This function creates a house instance with a "show" method
  and a "desc" subobject
*/
function house( rms, stl, yr, garp, desci ) {
     this.length = 5;               // four info props and length itself
     this.rooms = rm;               // rooms; prop [1]
     this.style = stl;               // style; prop [2]
     this.yearbuilt = yr;               // year built; prop [3]
     this.hasgarage = garp;               // garage?; prop [4]
     this.descr = desci;               // description instance; prop [5]
     this.show = mshowhouse;          // the showhouse method; prop [6]
     this.howold = howold;               // the howold method; prop [7]
}

In order to properly create a house instance we must first create a desc instance, and pass it as the fifth argument to house. It would be an error to pass in a desc object. A house instance, even one with a sub-object, must have all its slots filled in; this is what makes it an instance. This means that all the slots in the desc property of house must be filled in, as well, so that it, too, must be an instance. Once this has been done, it is possible to use all the properties and methods of the desc of the house. Listing 4.10 shows code that creates a desc instance, creates a house instance with that description, and then displays the color, age, and volume of the house using the properties and methods of the desc (and myhouse itself). This type of structure, in which objects and instances can be contained within one another, is referred to as an object hierarchy. Listing 4.10 is also found in the CD-ROM file house3.js. When this code is executed we obtain a page that looks like figure 4.3.

Figure 4.3 : Objects can contain one another in an object hierarchy.


Listing 4.10  house3.js  Creating and Using Sub-Objects

/*
  Create a desc instance and use it to create a house instance
*/
var mydesc;
var myhouse;
var mycol, myvol;
mydesc = new desc( 20, 18, 15, "beige" );      // fairly big; ugly color
myhouse = new house( 10, "Colonial", 1989, true, mydesc );  // mine, though
/*
  Display the colorvolume and age of the house using a reference
  to the desc properties of myhouse.
*/
mycol = myhouse.descr.color;               // property of property
myvol = myhouse.descr.findvolume(true);      // submethod
document.write("<BR>My house is " + mycol);
document.write("<BR>Its " + myhouse.howold( 1996 ) + " years old");
document.write("<BR>My house occupies " + myvol + " cubic meters");
document.write("<BR>");

The with Statement

Once you have become hooked on object-oriented programming, it often becomes a pervasive aspect of your coding style. Objects begin to show up everywhere. JavaScript has a convenient statement, borrowed from the Pascal language, that performs a set of object manipulations on the same object. Listing 4.10 may have impressed you with the power of its object manipulations. It may have also intimidated you a bit with the amount of typing that is required to get the color of the myhouse instance.

The purpose of the with statement is to permit a number of object references to be made to the same object (or instance) without having to repeat the name of that object. The format of the statement is

with ( objname ) {
     statements
     }

objname is the name of an object or instance. Inside the with block any reference to properties of objname occurs as if they had been prefixed with objname and the dot operator (.). Listing 4.11 shows an expanded version of the second part of listing 4.10, in which various aspects of myhouse are displayed. The mshowhouse method should now be extended to not only display the properties of its instance, but to also call a similar show method within the desc object (which will also need to be created).


Listing 4.11  Using the with Statement as an Implicit Object Reference

/*
  Display the color and volume of the house using a reference
  to the desc properties of myhouse.
*/
var mycol, myvol, myage;
with ( myhouse ) {
     mycol = descr.color;        // 1: ref to myhouse.descr.color
     myvol = descr.findvolume(true); // 2: ref to 
         myhouse.descr.findvolume
     myage = yearbuilt;        // 3: reference to myhouse.yearbuilt
     document.write("<BR>My house is " + mycol);                 // 4
     document.write("<BR>My house occupies " + myvol + " cubic meters");  // 5
// 6: explicit reference to another instance
     if ( myage > yourhouse.yearbuilt ) {
          document.write("<BR>Its newer than yours!");          // 7
          }
     document.write("<BR>");
     }

Each of the statements labeled 1, 2, and 3 makes an implicit reference to the myhouse object, which was established as the default object to use in the with statement. Note that not every statement within the with block needs to refer to myhouse. Statements 4, 5, and 7 make absolutely no reference to any house object. Also, statement 6 makes an explicit reference to a different house instance, namely yourhouse.

Statement 6 exposes one of the weaknesses of the with statement. When JavaScript careens through this with block, it must decide many times when the implicit myhouse is to be used, and when it is to be skipped. It must examine every reference, in fact. So, for mycol it must decide if you meant the local variable mycol or if there is some property of myhouse named myhouse.mycol. Fortunately, there is an unambiguous choice in every case. There is no mycol property of the house object.

Statement 6 uses an explicit reference to yourhouse. If statement 6 had been written as

if ( myage > yearbuilt ) {

JavaScript would have misinterpreted your intentions as to the meaning of yearbuilt, and would have implicitly translated this statement to

if ( myage > myhouse.yearbuilt ) {

since there is a yearbuilt property of myhouse. This type of error is both common and pernicious. Since JavaScript is an interpreted language, there is no way to see that this inappropriate translation has taken place. There is no compiled output that can be examined. Such errors are very hard to debug. Even though with is very useful, its use should be strictly circumscribed.

CAUTION
with blocks should be as short as possible. Check all statements within the with block to ensure that there are no ambiguous references to local variables or to properties of other objects.

Functions with a Variable Number of Arguments

Our discussion of the object foundations of JavaScript is almost complete. We have learned that functions are used to define objects and create instances using the new operator. We have also learned that objects, indexed arrays, and associative arrays are really all the same. In fact, the unity between all these concepts goes even deeper. JavaScript functions themselves have properties that can be used to fine- tune their behavior.

This aspect of JavaScript is still evolving at the time of this writing. However, we can say for certain that all JavaScript functions will have at least the following two properties:

The caller property is the name of whoever called the function. The arguments property is an array of all the arguments that are not on the argument list of the function. The caller property permits a function to identify and respond to the environment in which it is called. The arguments property allows us to write functions that take a variable number of arguments. The arguments in the function's argument list are mandatory, while those in the arguments property are optional. Listing 4.12 shows a function that takes one mandatory argument and a potentially unlimited number of option arguments. It returns a string describing its invocation. This function is contained in the CD-ROM file optarg.js.


Listing 4.12  optarg.js  A Function with Mandatory and Optional Arguments

 /*
 . Demonstrate mandatory and optional
  arguments to a function. Add all optional argument, and return the sum
  as a string.
*/
function addem( str1 ) {               // one mandatory argument
     var nopt = addem.arguments.length;     // # of arguments
     var sum = 0;                    // sum of optional arguments
     var strres;                    // string result
     for(  var i = 1; i < nopt; i++ ) {     // iterate over all 
                                                         optionals
          sum += addem.arguments[i];     // add them
     }
     strres = "Hello " + str1 +  ", sum is " + sum;
     return(strres);
}

To see how this works, suppose that this function is called from within another function named test1, with the following invocation:

var str = addem( "there", 1, 3, 5, 7 );

What happens? The mandatory argument "there" is assigned to the parameter str1 of the function addem. The complete argument list is also assigned to the variable length array addem.arguments. This has a length property (as do all well behaved arrays), which has the value 5 since there are five arguments all together-one mandatory argument and four optional arguments. This means that the local variable nopt is 5. Unlike the examples we have used, the length property is not at index 0 of the arguments array. The arguments begin at addem.arguments[0] and continue up to addem.arguments[4] (five elements total). This means that the optional arguments begin at addem.arguments[1]. The for loop in addem adds the optional arguments together, and arrives at 1 + 3 + 5 + 7 = 16, which is assigned to the local variable sum. Finally, strres is constructed by concatenating various strings, among them the mandatory parameter str1, which is "there", and the value of the sum. The concatenated string is returned, and assigned to str; its value is the string "Hello there, sum is 16."

Notice that both the mandatory argument str1 and the optional arguments are part of the argument list addem.arguments. Notice also that there need not be any optional arguments. The function call

var str = addem( "on a stick" );

returns the value "Hello on a stick, sum is 0."

Built-In Objects

Now that we have covered the foundations of object-oriented programming in JavaScript we can begin to look at the actual objects that JavaScript itself provides. These objects can be put into the following three categories:

Built-in objects include string objects, the Date object, and the Math object. They are referred to as built-in because they really do not have anything to do with Web pages, HTML, URLs, the current browser environment, or anything visual. HTML objects, in turn, are directly associated with elements of Web pages. Every link and anchor is a JavaScript object. Every form, and every element within a form, is an HTML object. The hierarchical organization of display elements on a Web page is reflected almost exactly in a hierarchical set of nested HTML objects. You've already gotten a taste of this hierarchy in the event processing examples of chapter 3.

Browser objects are at the top of JavaScript's object hierarchy. These objects represent large scale elements of the browser's current environment, and include objects such as window (the current window), history (the list of previously visited pages), and location (the URL of the current page).

The rest of this section briefly describes the built-in objects of JavaScript. The next two sections give overviews of the HTML and browser objects. Each of these three categories is quite rich, and chapters 5, "Built-In JavaScript Objects," through 7, "Advanced HTML Objects and Navigation," provide more in-depth information on each of the three categories.

String Objects

String objects are the most built-in of all the built-in JavaScript objects. You do not even use new when creating a string object. Any variable whose value is a string is actually a string object. Literal strings such as "HelloWorld" are also string objects.

String objects have one property, length, and many methods. The length property gives the length of the string. The methods fall into three categories: methods that manipulate the contents of the string, methods that manipulate the appearance of the string, and methods that convert the string into an HTML element.

String Content Methods

The following methods can be used on string objects to access, control, or modify their content:

The toLowerCase and toUpperCase methods convert the contents of the string entirely to lower- and uppercase, respectively. So if we define the string variable

var mystr = "Look At This"

then its length property, mystr.length, will have the value 12, since there are 12 characters in the string. In addition, we can apply the two case conversion methods and get

mystr.toLowerCase() = "look at this"
mystr.toUpperCase() = "LOOK AT THIS"

These two functions do nothing to characters that have no case, so the two spaces in this string are unchanged. We could have also applied the methods directly to the literal form of this string object, so "Look At This".toLowerCase is also equal to "look at this".

The methods charAt and substring are used to extract either a single character from a string, at position idx, or to extract a range of characters, from position fromidx up to but not including position toidx. Character positions are zero-based, as are all JavaScript arrays, so that all indices must fall between 0 and one less than the length of the array. For example, using mystr, we have

mystr.charAt(5) = "A"
mystr.substring(5,7) = "At"

Like the method functions toUpperCase() and toLowerCase() these methods both return strings. Care should be take to give these methods valid indices that are actually within the string. The substring method will forgive you if you accidentally specify a toidx which is <= the corresponding fromidx-it will return the empty string "".

Finally, both the indexOf and lastIndexOf methods are used to search for chr with a string. indexOf searches from the beginning (left side) of the string and lastIndexOf searches from the end (right side). Both return an integer index if they find the character, and -1 if they do not. Using mystr again, we can search for the character o from both sides:

mystr.indexOf("o") = 1
mystr.lastIndexOf("o") = 2

The first search finds the first o of the word "Look" at position 1 (second character), and the second search finds the second o of "Look" since that is the first o when searching from right to left. Both of these methods also take an optional second argument that specifies an initial index at which to start the search.

String Appearance Methods  The string appearance methods are used to control how a string appears when displayed on a Web page. If you are creating a page with standard HTML tags you would achieve the same effects by using various tags. For example, to make the string "help" appear in italics you would write <I>help</I>. The string appearance methods allow you to obtain the same effects in JavaScript without using the corresponding HTML elements. The string appearance methods are as follows:

Most of these methods should be self-explanatory. The italics method, for example, performs exactly the same function as the I tag in HTML. The only two that take arguments are the fontcolor and fontsize methods. The fontcolor method changes the font color of the string, as if the <FONT COLOR=colr> attribute had been size. Similarly, the fontsize method changes the size of the font used for displaying a string as if the <FONT SIZE=sz> attribute had been given. colr should be a string; sz may be a number or a string. If it's a number then this specifies an absolute font size; if it's a string such as "+2" it specifies an increment relative to the current font size. Listing 4.13 shows several examples using the string appearance methods. The output of this code is shown in figure 4.4.

Figure 4.4 : Many HTML style tags have equivalent JavaScript methods.

NOTE
Not all HTML style tags have corresponding string appearance methods. You can always directly embed an HTML tag in the string itself if there is no method with the same functionality.


Listing 4.13  String Methods Can Be Used to Change How Strings

Are Displayed

var bstr = "big";
var sstr = "small";
/*
  This displays strings with both big and small text.
*/
document.write("<BR>This is " + bstr.big() + " text");
document.write("<BR>This is " + sstr.small() + "text");
/*
  The following two strings contain directly embedded HTML tags.
  They have exactly the same result as the two method calls above
*/
document.write("<BR>This is <BIG>big</BIG> text");
document.write("<BR>This is <SMALL>small</SMALL> text");
/*
  If your favorite tag does not have a method, just embed it
*/
document.write("<BR>This is <STRONG>strong</STRONG> text");
document.write("<BR>");

HTML String Methods

JavaScript provides two string methods for converting strings into hypertext entities. These methods should be clearly distinguished from the HTML objects, such as forms, which are discussed in the section "Browser and HTML Objects" later in this chapter. These methods are used to create HTML, while the HTML objects already are HTML. The two methods in this category are as follows:

Both these methods are used to create some form of the anchor (<A>) HTML attribute. The difference between them is that the anchor method is used to create an anchor with namestr as the value of the NAME attribute, while link is used to create an anchor with the HREF attribute set to hrefstr. Said another way, anchor creates an anchor that is a target, while link creates an anchor that is a link. Both methods convert the string on which they operate into the text portion of that anchor. namestr may be any valid string which may be a NAME, so it should not have any embedded white space. hrefstr should be a valid URL, since the user is being invited to click it. Listing 4.14 uses these methods and shows a simple example that sets up an anchor target and then links to it.

NOTE
The anchor() string method uses the older but more common HTML NAME attribute rather than the newer ID tag.


Listing 4.14  String Methods Can Be Used to Create HTML Anchors and Links

var sum4str = "Summary of Chapter 4";
var sum4tar = "Summary4";
/*
  Create a summary target and a link to it. The following two
  statements are completely equivalent to this HTML:
  <A NAME="Summary4">Summary of Chapter 4</A><HR>
  Click here for a <A HREF="#Summary4">Summary of Chapter 4</A>
*/
document.write(sum4str.anchor(sum4tar));
document.write("<HR>");
document.write("Click here for a " + sum4str.link(location + "#" +  sum4tar));
document.write("<BR>");

The Math Object

The Math object is used for various forms of mathematical calculations. It contains several properties that are standard constants, such as pi = 3.14159…, as well as a large set of methods that represent common trigonometric and algebraic functions. All Math methods deal with floating-point numbers. Angles are expected to be given in radians, not degrees.

The Math object is our first example of a static object. A static object is one that does not change. All of the slots in the Math object already have values. This makes perfect sense, since you cannot change the value of pi or invent a new meaning for the cos() function (not without creating chaos). The practical consequence of Math being static is that you never use new with Math; you always refer to the Math object directly. Math is the opposite of the String object. The String object has instances but no explicit object; the Math object has only itself, and no instances.

The Math object has the following properties:

The Math object has the following methods:

These are all the functions and constants you find on any decent calculator. Remember that JavaScript is case-sensitive, so you must write Math.PI exactly to get the value of pi. The constants stand for the base of the natural logarithm (Napier's constant, or about 2.71828), the natural log of 10 (about 2.30259), the natural log of 2 (about 0.69315), everyone's favorite pi (about 3.141592653589793), the square root of 1/2 (about 0.7071), and the square root of 2 (about 1.4142).

The methods of the Math object include the common trigonometric functions, including the sine (sin), cosine (cos), tangent (tan), and their inverses, the arcsin (asin), arccos (acos), and arctan (atan). Each of the trig functions takes an angle in radians and produces a floating-point number. The values should be between -1 and 1 for the sin and cos methods. Each of the inverse trig functions takes a number, which should be between -1 and 1 for the asin and acos methods, and returns an angle in radians.

The ceil, floor, and round methods all take floating-point numbers as inputs, and return integers as outputs. The ceil method gives the smallest integer that is greater than or equal to its argument, while floor returns the largest integer that is less than or equal to its argument. The round method gives the nearest integer.

The exp, log, pow, and sqrt methods all deal with exponentiation or its inverse. The exp method raises Math.E to the power given by its argument, and is the inverse of the log method, which returns the natural logarithm of its argument, which should be positive. The pow method raises num1, its first argument, to the power num2, its second argument. The sqrt returns the square root of its argument. If you inadvertently give sqrt a negative number it forgives you and returns 0.

Finally, the abs, min, max, and random methods perform various useful operations. The abs method returns the absolute value of its argument. min and max give the minimum and maximum value of their two arguments, respectively. The random method takes no arguments. It returns a random, floating-point number between 0 and 1. For some obscure reason the random method is only available in the UNIX releases of Netscape Navigator 2.0. Listing 4.15 presents some simple uses of the Math object. This example can be found in the CD-ROM file mathex.js.


Listing 4.15  mathex.js  Three Useful Functions Using the Math Object

/*
  Compute the area of a circle given its diameter
*/
function areaofcir(diam) {
     var radius = diam / 2;
     return( Math.PI * radius * radius );     // pi times r squared
}
/*
  Given the coordinates of a point on a circle,
  determine how far around the circle we must rotate in order
  to reach that point. Return the angle in radians.
*/
function angoncircum( x, y ) {
     var epsilon = 0.00001;     // a very small number
     if ( Math.abs(x) < epsilon ) {          // if x is very close to zero
          if ( y > 0 ) {                    // positive x axis
               return(0.0);               // 0 degrees = 0 radians
          } else {                    // negative x axis
               return( Math.PI );          // 180 degrees = pi radians
          }                         // end of inner if-else
     }                              // end of outer if
// division by zero avoided by the "if" test above
     return( Math.atan( y / x ) );
}
/*
  Given the diagonal size of a television, compute its width
  assuming that the screen is square
*/
function tvsize( diag ) {
     return( diag / Math.SQRT2 );
}

The Date Object

Dealing with dates is one of the most tedious tasks in any language. This is because many people like to represent dates and times in decidedly nondecimal systems. Months come in units of 12, hours in units of 24, and minutes and seconds in units of 60. All these variations are quite illogical from the computer's standpoint. It likes to deal with nice, round numbers, preferably powers of 2, or at least multiples of 10.

The Date object simplifies and automates a lot of the conversion woes associated with going back and forth between a human readable representation, such as November 23, 1990, and the internal representation. JavaScript's Date object follows the UNIX standard of storing date and time information internally as the number of milliseconds since January 1, 1970. This date is often called "The Epoch," since it is shortly after UNIX was first unleashed on an unsuspecting world.

CAUTION
The current version of JavaScript does not permit you to manipulate dates earlier than The Epoch. Attempting to do so gives unexpected and incorrect results.

The Date object has no properties, but many methods. In order to use the Date object you must first understand how to construct instances of it. There are three basic methods of creating a Date instance, as follows:

The first form constructs a Date instance that represents the current date and time. This should be accurate to within a second, and also include information about your time zone and any corrections to it currently in effect (such as Daylight Savings Time). The second form takes a string of the form "Month Day, Year" such as "November 23, 1990" and converts it to a Date instance. This string may optionally have a time of the form HH:MM:SS at the end, which is used to set the time to HH hours, MM minutes, and SS seconds. Hours should be specified using a 24-hour clock, also known as military time, so that 10:15 PM is represented as 22:15:00. The third form takes three integers representing the year, month, and day. Note that the month is always indexed from zero, so that November is month 10. The year can also be offset by 1900, so that you can use either of these two forms

var ndat = new Date(90, 10, 23);
var ndat = new Date(1990, 10, 23);

to create a Date instance named ndat for November 23, 1990. Note that for the year 2000 and beyond you must use the second form. This form may optionally take an additional three integer arguments for the time, so that 1:05 PM on November 23, 1990 is

var ndat2 = new Date(90, 10, 23, 13, 5, 0);

The Date object has a large set of methods for getting and setting the components of a date. These methods are as follows:

Most of these methods perform the obvious operation on their Date instance. nvar.getMonth() returns 10, representing November. It is 10, rather than 11, because months are zero-indexed, so that the value of getMonth() is always between 0 and 11, inclusive. The confusingly named getDate, getDay, and getTime are worth a slightly closer look. The getDate method returns the day of the month (1-31), the getDay method returns the day of the week (0-6), and the getTime method returns JavaScript's internal representation of the date, namely the number of milliseconds since The Epoch. This last method might seem to be of dubious utility, but it is useful for comparing two dates to see which is later. The set methods are, of course, used to set the various components of a Date instance. Listing 4.16 shows two simple date manipulation functions. This code can be found in the CD-ROM file datex.js.


Listing 4.16  datex.js  Two Useful Functions Using the Date Object

/*
  Given a date as a string, return the day of the week
 as an integer between 1 and 7. Note Sunday = 1.
*/
function dayofweek( datestr ) {
     var dati;
     dati = new Date( datestr );          // make datestr into a Date 
    instance
     return( 1 + dati.getDay() );          // get the day of the week 
      and add 1
}            // since getDay() returns a number between 0 and 6
/*
  Compute the number of days to your birthday. Your birthday is speci-  
  fied as the day and month.
*/
function tobday( dayi, moni ) {
     var today, todayy, todayms;
     var you, youms;
     var tdiff;
     today = new Date();               // today's date
     todayy = today.getYear();          // current year
     you = new Date(todayy, moni-1, dayi);     // your birthday this 
// need to subtract 1 because months are zero-indexed
     todayms = today.getTime();          // convert today to ms since 
  The Epoch
     youms = you.getTime();               // convert your birthday to ms 
    since The Epoch
     if ( youms < todayms ) {          // if your birthday has already 
              passed..
          you.setYear(1 + todayy);     // look forward to next year
          youms = you.getTime();          // recompute ms since The 
    Epoch
          }
     tdiff = youms - todayms;          // number of milliseconds until 
              your next birthday
     tdiff /= 1000;                    // convert to seconds
     tdiff /= 60;                      // minutes
     tdiff /= 60;                      // hours
     tdiff /= 24;                      // convert to days
return( Math.round( tdiff ) );     // round to nearest integer
}

In addition to the get and set methods, the Date object also has methods for converting a Date instance to a string, and two static methods for parsing dates. These methods are as follows:

The first three of these methods convert a date instance into a string representing the date and time relative to Greenwich Mean Time (GMT, also called UTC for Universal Coordinated Time), relative to the current date formatting conventions (which vary between Europe and the U.S., for example), and as just a plain, ordinary string, respectively. The last two methods are used for converting date strings in local time (parse method) or in UTC time (UTC method) into the number of milliseconds since The Epoch. These methods must be referenced as Date.parse() and Date.UTC() since they are static; they may not be used with Date instances. Since they return the internal representation of dates, these values are often simply passed to setTime.

TROUBLESHOOTING
I have modified your function tobday in listing 4.16. My version accepts an arbitrary string as the input birthday. It works perfectly for my birthday, but it fails horribly for my father's birthday. What is wrong? The code looks like:
function tobday2( bdaystr ) {
var bdayint = new Date( bdaystr );
... many lines of code not shown
Since your father was undoubtedly born before January 1, 1970, the very first line attempts to create a Date instance corresponding to a date before The Epoch. This is not currently permitted. Since it seems that you were born after The Epoch, the code will work fine for your birthday. Until this restriction is lifted you must convert the year to one after 1970 before you construct a Date instance.

Built-In Functions

You have now had your first exposure to the built-in String, Math, and Date objects. Some of these objects are more built-in than others. While Date acts like an actual object, with the exception of its two static methods, the String object is almost invisible. All normal JavaScript programs manipulate strings as if they are a separate data type. The essence of a string is part of the JavaScript language.

There is also a small set of functions built in to JavaScript itself. They are not methods, and are never applied to an instance using the dot operator (.). They are on the same plane as functions that you create using the function keyword. At present, there are five such built-in functions; they are as follows:

The escape and unEscape functions are used to convert to and from the escape code convention used by HTML. In HTML a number of special characters, such as the HTML delimiters < and >, must be represented in a special way to include them in ordinary text. For example, if you have written any HTML at all then you know that you sometimes need to write %20 to represent a space character. The escape built-in function takes a string representing one of these special characters and returns its escape code in the form %xx, where xx is a two-digit number. Thus, escape(" ") returns %20, the code for a space character. The unEscape function is the inverse of the escape function. It takes an escape code and returns the char-acter which that code represents. Thus unEscape("%20") returns the string " " (a single space character).

The parseFloat built-in function attempts to parse its string argument as a floating-point number. It only continues parsing the str until it encounters a character that could not possibly be part of a valid floating-point number, such as g. The parseInt built-in function performs a similar operation. It attempts to parse its str argument as an integer in base radix. Thus we would obtain the following values:

parseFloat("+3.14williamtell5") = 3.14
parseInt(10111, 2) = 23

Note that everything after the first w is ignored, since w cannot possibly be part of a floating-point number. The second value is obtained because 23 in binary (base 2) notation is 10111.

Finally, the eval function attempts to evaluate its string argument as a JavaScript expression and return its value. All the normal rules for evaluating expressions, including variable substitution, are performed by the eval function. This function is extremely powerful simply because it evaluates any JavaScript expression, no matter what that expression does. You will see a lot more of this function in several subsequent chapters. For the moment, we briefly look at a simple example in which we ask eval to do some arithmetic for us. If x is a var with the value of 10 then the following two expressions assign 146 to both y and z:

y = ( x * 14 ) - ( x / 2 ) + 11;
z = eval("( x * 14 ) - ( x / 2 ) + 11");

Browser and HTML Objects

The JavaScript object model and its very interesting set of built-in objects, methods, and functions provide what we would expect from any modern programming language. They provide control structures, encapsulation, functions, mathematical operations, and so forth. Since JavaScript is designed to work with and on the World Wide Web there must also be a linkage between it and the contents of HTML pages.

This linkage is provided by JavaScript's extremely rich set of browser and HTML objects. The browser objects are a reflection of the browser environment, and include objects that can be used to reference the current page, the history list, and the current URL. There are also methods for opening new windows, putting up dialog boxes, and writing HTML directly. We have already been leaning heavily on one such method, the write method of the document object.

The browser (or navigator) objects are at the top of JavaScript's object hierarchy, since they represent overall information and actions that are not necessarily associated with a particular Web page. Within a given Web page, however, each HTML element has a corresponding object, an HTML object, within the object hierarchy. In particular, every HTML form, and every HTML element within every form, has a corresponding object. Figure 4.5 gives an overview of the JavaScript object hierarchy.

Figure 4.5 : JavaScript browser and HTML objects refer to all elements of a Web page.

In this section we briefly describe the key JavaScript browser and HTML objects, and show how they relate to one another. Most of the subsequent chapters in this book are devoted to in-depth discussions of how you can make these objects work for you. Each chapter in part II, "JavaScript Objects," in fact, is devoted to a particular category of JavaScript object (built-in, browser, or HTML). The purpose of this section, then, is to acquaint you with the structure of these objects without going into too much detail on how they are used.

Browser Objects

The primary browser objects, in rough order of significance, are as follows:

The window Object

The window object, as figure 4.5 shows, is the top object in the JavaScript object hierarchy. Every browser window that is currently open will have a corresponding window object. All the other objects are children of one of the window objects. In particular, every window is associated with a particular Web page, and the HTML structure of this page is reflected in the window's document object. Every window corresponds to some URL; that URL is reflected in the location object. Every window has a history of the previous pages that have been displayed in that window, which are represented by the various properties of the history object.

JavaScript maintains an idea of the current window, so that almost all references to sub-objects of the current window do not need to refer to it explicitly. This is why all of our output has been done using document.write() rather than window.document.write(). window objects have the following interesting methods (among others):

All these methods are used to manipulate the window state of the browser itself. The alert and confirm methods are used to display their msgstr argument in a dialog box. The alert method is used to alert the user to something about which the user can do nothing. An alert dialog box contains a single OK button. The confirm dialog box is more flexible, and displays its message with both an OK and a Cancel button. If the user selects OK then the confirm method returns true, otherwise it returns false. The prompt method is used to solicit user input, in the form of a string. It displays a dialog box with the msgstr and an editable text field. This method also accepts a second optional argument that can be used to set a default value in the input field. This method returns whatever the user typed as a string.

You use the open method of the window object when you wish to open a new browser window. The urlstr argument is a string representing the URL that will be loaded into that window. The wname argument is a string that gives the new window its name. This method returns an instance of the window object representing the new window created. This method also accepts a third argument that can be used to specify a wide variety of display options for the new window (such as whether or not it should display its toolbar). When the close method is invoked from a window instance the underlying window is closed and the URL in it is unloaded.

The document Object

Every window is associated with a document object. The document object contains properties for every anchor, link, and form on that page, as well as all of the sub-elements of those elements. It also contains properties for its title (the content of the <TITLE> field of the page), its foreground color (the fgColor property), its background color (the bgColor property), its various link colors, and other attributes of the page itself. The document object has the following methods:

The clear method is used to completely erase a document window. The entire contents are wiped out, regardless of how they got there. The clear method is particularly useful if you are constructing a Web page entirely within JavaScript, and want to make sure it is empty before you start. The open and close methods are used to start and stop buffered output. If you call the open method, perform a series of writes and/or writelns, and then call the close method, the results of your write operations are layed out and appear on the page.

CAUTION
Do not confuse the open and close methods of the document object with the window methods of the same names. They perform very different functions, and are not interchangeable. Use an explicit reference-document.open() or window.open()-to obtain the appropriate one.

Of course we are intimately familiar with the write method by now. The write method is used to write any string expression, including one containing embedded HTML, to the current document. Note that the write method actually takes a variable number of arguments, rather than just one. If more than one argument is given, each of the arguments is interpreted as a string and written in turn. The writeln method is identical to the write method, except that it outputs a carriage return after it has completed writing its argument(s). Note that this carriage return will be ignored by HTML, which really does not like embedded white space, unless the writeln is inside preformatted text (within PRE>...</PRE tags).

The history and location Objects

The history object is used to refer to the history list of previously visited URLs. The history object has a property known as length, which indicates how many URLs are stored on the history list at present. It also has the following three methods:

The go method is used to navigate the history list. The where argument can be a number or a string. If the where argument is a number then it indicates how far we wish to move in the history list. A positive number means that we wish to move that many documents forward in this history list, while a negative number is used to move backward. Thus, go(5) has the same effect as using the Forward button five times, while go(-1) would be the same as clicking the Back button once. If where is a string representing a URL then that URL is loaded, and becomes the current document.

The location object describes the URL of a document. It has properties representing the various components of the URL, including its protocol part, its hostname part, its pathname part, and its port number part, among other properties. Unfortunately, these properties are often null, at least in the UNIX version of Netscape Navigator 2.0. It also has a toString method which can be used to convert it to a string. We can use the following code to display a formatted message giving the current URL:

var loc = document.location;
document.write("<BR>URL is " + loc.toString());
document.write("<BR>");

HTML Objects

To understand how HTML objects work in JavaScript, let us consider a simple piece of HTML that creates an anchor, a small form, and a link to that anchor. This is not intended to be the HTML for a meaningful Web page, but it will nevertheless illustrate the correspondence between HTML elements and JavaScript HTML objects. Our elementary HTML code is given in listing 4.17. This code can be found in simple.htm on the CD-ROM.


Listing 4.17  simple.htm  HTML Code for a Page with a Form, Anchor, and Link

<HTML>
<HEAD>
<TITLE>A very simple HTML page</TITLE>
</HEAD>
<BODY>
<A NAME="top">This is the top of the page</A>
<HR>
<FORM METHOD="post" ACTION="mailto:nobody@dev.null">
<P>Enter your name: <INPUT TYPE="text" NAME="me" SIZE="70">
</P>
<INPUT TYPE="reset" VALUE="Oops">
<INPUT TYPE="submit" VALUE="OK">
</FORM>
<HR>
Click here to go to the <A HREF="#top">top</A> of the page
</BODY>
</HTML>

This code creates an HTML page with an anchor at the top of the page and a link to that anchor at the bottom. In between is a simple form that allows the user to enter his name. There is a submit button if he gets it right, and a reset button if he doesn't. If the user is successful the form's contents are submitted via a post action to the fictitious e-mail address nobody@dev.null.

The important aspect of this example is not its primitive HTML, but the fact that the HTML elements in it are reflected in the JavaScript object hierarchy. We have already seen that we can access the title of this document through the title property of the document object. We can also access the other HTML elements of this document using the following properties:

These properties of the document object are arrays representing every HTML element that is an anchor, form, or link on the page. In our particular example there is only one of each, so we would refer to the anchor at the top of the page as document.anchors[0], the link at the bottom of the page as document.links[0], and the form in the middle of the page as document.forms[0]. These are the top-level HTML objects represented by this document. Each of these elements, in turn, has properties and methods that can be used to describe and manipulate it.

In particular, the form object corresponding to forms[0] has sub-objects for each of the three form elements (the reset button, the submit button, and the text input field), as well as properties for the submit method and the submit target. forms[0].elements[0] corresponds to the text input field. forms[0].elements[0].name is the name of that field, as specified by the NAME field, which is "me" in this case. Figure 4.6 recapitulates this HTML code and shows how each element in the page is associated with an HTML object. We will have many more examples of this in subsequent chapters.

Figure 4.6 : Anchors, links, forms, and form elements are represented as objects in JavaScript.