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 backyard. 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 JScript, it's necessary also to talk of two levels. The basic aspects of the way JScript handles objects is known as its object model. The embellishments constitute the extensive set of features of the predefined objects in JScript, as well as those aspects of the language that can be used to create and use new, user-defined objects.
Before we can delve into object-oriented programming in JScript, 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 "JScript: The Language." This section takes you further, and explains several critical and often misunderstood ideas.
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, JScript refers to the items collected within an object as its properties. You may also recall that JScript 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 JScript Date object is a perfect example of the benefits of this kind of organization. As the name implies, a JScript 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 make 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 a typeless language such as JScript, 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 not 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.
Note |
Do not confuse the concept of a generic object with the much more specialized type of embedded object created with the HTML OBJECT tag. The latter is a considerably more advanced topic, which will not be discussed until Part III of this book, "Live Content and JScript." |
Now that we have presented the basic object foundation upon which JScript rests, it is time to consider how these concepts are implemented. How does one create objects and instances in JScript? 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[0] = rms; // number of rooms (integer) this[1] = stl; // style, e.g. Colonial, Tudor, Ranch (string) this[2] = yr; // year built, integer this[3] = 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 "JScript: The Language," it might have seemed mysterious how a function could actually do useful work without a return statement, because 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, because it tells JScript 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 );
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 such as Basic 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.
In the preceeding example, we created a new instance of the house object named myhouse.The instance myhouse is treated just like any other variable. It must be declared using var. Now that myhouse has been created, how do we refer to its properties? The [] notation in the house function looks suspiciously like an array reference. In fact, JScript also has arrays that use zero-based indexing. In JScript, however, arrays and objects are really two views of 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
The various elements of the myhouse
array contain its properties, which can be accessed by their numerical
indices. myhouse[0] has the
value 10, myhouse[1] is the
string "Colonial,"
myhouse[2] is 1989,
and myhouse[3] is the Boolean
value true. The fact that
the number of rooms and year
it was built are integers, the style
property is a string, and hasgarage
property 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 JScript. The new operator does not protect you against inadvertently assigning an inappropriate value to a property. |
In this form of object, it is possible to access the properties sequentially, which is often very useful. If we know that house objects always have four members, 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 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 ). Because 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 several pieces of implicit knowledge. We must remember that every house instance has exactly four properties, and we must remember which property is associated with each index. If we were to mistakenly use the myhouse[2] property as the hasgarage Boolean, rather than the yearbuilt integer, an error would no doubt result. In addition, if we 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, the showhouse function would print only the first four properties, and would never print the taxrate.
An even more disastrous error would occur if we redefined 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, because it refers to an array element that was not within the boundaries of the array.
Fortunately, JScript provides a simple means to address these issues. In JScript, it is possible to create arrays which are indexed by names, rather than numbers. We can rewrite the definition of the house object to reference its properties by name. This code for the new house object 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 Properties
/* This function creates a house instance whose first property contains the number of properties in the house instance. All properties are referenced by name. */ 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 house object function takes four parameters, as before. It sets its length property to this number plus 1, because there are four meaningful properties (rooms, style, yearbuilt, and hasgarage) and the length property itself. More importantly, each of the properties is now referenced by name. If we create a myhouse instance, as before, we can now refer to myhouse.rooms, whose value is 10, myhouse.style, whose value is "Colonial", myhouse.yearbuilt, whose value is 1989, and myhouse.hasgarage, whose value is true. We can also use myhouse.length, whose value is 5, to tell us the total number of properties. This method of accessing instance members using the dot operator (.) is known, naturally enough, as dot notation.
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 (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 of the showhouse
function. The doctrine of encapsulation says that this is bad.
The length of an object should be stored only 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" section later in
this chapter describes how to do this.
Tip |
Define all objects with a length property, which gives the number of properties in the object. Make the length property the first property. |
It might seem that the two types of house objects, from Listings 4.1 and 4.3, are very different. In fact, JScript provides a powerful method by which objects created using the dot notation may be treated as arrays. Object properties may be referred to not only as indexed array elements but also as named array elements. This is why the JScript objects are also known as associative arrays. The set of properties of the myhouse instance created from Listing 4.3 could also be written as:
myhouse["length"] = 5; myhouse["rooms"] = 10; myhouse["style"] = "Colonial"; myhouse["yearbuilt"] = 1989; myhouse["hasgarage"] = true;
Following the preceeding advice, the first property of this object
is its length, which we now know can be referred to as either
myhouse.length or myhouse["length"].
We cannot refer to it as myhouse[0],
however, even though it is the first property. Earlier versions
of the JScript language, such as the original JavaScript that
came with Netscape Navigator 2.0, did allow all three forms of
access. The current implementation of JScript does not permit
associative array elements to be accessed using numerical indices.
This means that the showhouse
function in Listing 4.2 will not work with the myhouse
object created using Listing 4.3. The for...in
statement, described as follows, will allow us to rewrite showhouse
to work with the associative array form of house
instances.
Caution |
JScript arrays can be accessed either by integer indices or by property names, but not both. Property names are case-sensitive. Integer indices are limited by the length of the array. If you refer to nonexistent array elements, by name or by index, this either generates a JScript error or gives you an invalid value (usually null). |
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 also to 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 and the hasgazebo (Boolean) property. Dynamic extensions apply only 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, as follows:
pizza = new house( 3, "Restaurant", 1993, false );
it will not have a sheds property nor 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 ). Because 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 always to 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 named property, mystringarr.length, is the all-important length property. The next 100, at numerical 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). Note that we could print the entire contents of this array by using a for statement such as:
for(var oi = 1; oi <= mystringarr.length; oi++) document.write("Element " + oi + " is " + mystringarr[oi] + "<BR>");
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;
Chapter 2 "JScript: The Language," introduced 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 that 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 that will work with associative arrays, and that 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>"); }
One of the most powerful aspects of object-oriented programming in JScript 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 that 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 this.style = stl; // style this.yearbuilt = yr; // year built this.hasgarage = garp; // garage? this.show = mshowhouse; // the showhouse method } /* 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 var idx = 0; for ( var iter in this) { //iterate if ( idx < 1 || idx >= nprops ) { idx++; continue; // skip "length" and "show" } document.write("<BR>Property " + idx + " is " + this[iter]); idx++; // increment numerical index} 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 deliberately did not count this method in the length of the property array: The value of the length property is 5, even though there are six properties.
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. We have deliberately shortened the length property by one and used an auxiliary variable named idx so that we can display the numerical index of each property. Only the first four properties of the house instance 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.
Because this function takes no arguments, you might wonder how it is used. The answer is that because 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 in the same way as their nonmethod counterparts:
showhouse( myhouse );
showhouse( yourhouse );
showhouse( pizza );
This particular method function takes no arguments, and is 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 from 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 operate only on instances of an object, then those functions should be methods of that object. |
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 JScript is typeless, does this mean that objects can contain other objects? In a word, yes.
Suppose we create a new object called desc, which 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 this.width = wd; // width of the thing this.height = ht; // height of the thing this.color = col; // color 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 teacozy. 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 within the house, car, boat, and teacozy 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 the house object creation function.
Listing 4.9 house3.js-The house Object with a desc Subobject
/* 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 this.style = stl; // style this.yearbuilt = yr; // year built this.hasgarage = garp; // garage? this.descr = desci; // description instance this.show = mshowhouse; // the showhouse method this.howold = howold; // the howold method }
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 subobject, 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.
Listing 4.10 house3.js-Creating and Using Subobjects
/* 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>");
Figure 4.3 : Objects can contain one another in an object hierarchy.
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. JScript 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 not only to display the properties of its instance, but also to 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 need 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 JScript 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 whether you meant the local variable mycol, or whether 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 ) {
JScript would have misinterpreted your intentions as to the meaning of yearbuilt, and would have implicitly translated this statement to:
if ( myage > myhouse.yearbuilt ) {
because there is a yearbuilt
property of myhouse. This
type of error is both common and pernicious. Because JScript 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. |
Our discussion of the object foundations of JScript 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 indexed arrays and associative arrays are both really objects. In fact, the unity between all these concepts goes even deeper. JScript functions themselves have properties that can be used to fine-tune their behavior.
This aspect of JScript is still evolving at the time of this writing. However, we can say for certain that all JScript functions will have at least the following two properties:
The caller property is a string containing 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 enables 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, because 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."
Now that we have covered the foundations of object-oriented programming in JScript, we can begin to look at the actual objects that JScript itself provides. These objects can be put into the following three categories:
Built-in objects include the string objects, the Date
object, the Math object,
and the Array 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 JScript
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 "Events and
JScript."
See Chapter 3 "Events and JScript" for more information on the relationship between HTML elements and JScript functions, p. 59. |
Browser objects are at the top of JScript'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 JScript. The next two sections give overviews of the HTML and browser objects. Each of these three categories is quite rich. Chapter 5 "Built-In JScript Objects," Chapter 6 "Interactive HTML Objects," and Chapter 7 "Advanced HTML Objects and Navigation," provide more in-depth information on each of the three categories.
String objects are the most built-in of all the built-in JScript objects. You do not even need to 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. You can also use new on the string object to obtain a new string. The following statements both construct a string whose value is "hiya":
var mystring = "hiya"; var mystring2 = new String("hiya");
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 either to extract 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 JScript 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" because 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 JScript 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 used. 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, 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.
These methods are described more fully in Chapter 5 "Built-In JScript Objects," p. 123. |
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>");
Figure 4.4 : Many HTML style tags have equivalent JScript methods.
HTML String Methods. JScript 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 on "Browser and HTML Objects" later in this chapter. These two 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, because 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 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, because you cannot change the value of pi or invent a new meaning for the cos() function (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. Static objects, such as the Math object, are different from "ordinary objects," which have themselves as well as their instances. 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 JScript 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).
Note |
Internet Explorer 3.0 does not yet support the properties Math.LN2E and Math.LN10E provided by Netscape Navigator 3.0. |
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. Although older releases of IE did not
support random(), all current
versions do. Listing 4.15 presents some simple uses of the Math
object. This example can be found in the CD-ROM file mathex.js.
More detailed examples using the Math object can be found in "The Math Object" section of Chapter 5 "Built-In JScript Objects," p. 128. |
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 ); }
Dealing with dates is one of the most tedious tasks in any language. This is because many humans 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
Nov 23, 1990, and the internal representation. JScript'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," because it is
shortly after UNIX was first unleashed on an unsuspecting world.
Caution |
The current version of JScript 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 P.M. 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 argument for the time, so that 1:05 P.M. 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 JScript'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 specified as the day and month. Note that your birthday is assumed to occur at midnight, so that if today is your birthday it will be reported as a year away.*/ 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 year // 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() because they
are static; they may not be used with Date
instances. Because they return the internal representation of
dates, these values are often simply passed to setTime.
The methods of the Date object are explored more thoroughly in the last two sections of Chapter 5 "Built-In JScript Objects," p. 132. |
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 ) { Because 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. Because 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, or simply omit the year entirely. |
The array object is an extremely simple built-in object which is used to construct an array of empty slots. Any instance of the Array object will always have a length property, and will also have as many elements as are specified in the constructor. For example, the statement:
var myarr = new Array(10);
constructs an array instance named myarr. The value of myarr.length with be 10, and the properties myarr[0] through myarr[9] will all be NULL. The array object can often be used as a simple object creation function when the values in the array are going to be initialized later. One of the most convenient things about this built-in object is the fact that it always creates a length property.
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 JScript programs manipulate strings as if they are a separate data type. The essence of a string is part of the JScript language.
There is also a small set of functions built into JScript 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, which 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, you know that you sometimes need to type %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 character that 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 continues parsing the str only 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, because 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 JScript 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 JScript 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");
The JScript 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 JScript 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 JScript'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 JScript's object hierarchy, because 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 JScript object hierarchy.
Figure 4.5 : JScript browser and HTML objects refer to all elements of a Web page.
In this section, we briefly describe the key JScript 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, "JScript Objects," in fact, is devoted to a particular category of JScript 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.
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 JScript 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.
There are several special window objects worth noting. The top window refers to the highest-level window on the current Web page. The self window refers to the current window, while parent refers to that window's parent, if any. JScript tries to keep track of the current window, so that almost all references to subobjects of the current window do not need to refer to self explicitly. This is why all of our output has been done using document.write() rather than self.window.write() or 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, 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.
Several excellent examples of the open and close methods can be found in the "Dynamic Documents" section of Chapter 8 "Dynamic HTML and IE Objects," p. 228. |
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 subelements 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 JScript, 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 or writelns,
and then call the close method,
the results of your write
operations are laid 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).
Note |
Internet Explorer 3.0 has a bug in the implementation of document.write() and document.writeln(). The output of these functions is not appended at the end of the current document. A new document is always created, and the output written there. In addition, if no document.close() is used after a write operation, it may be necessary to press the Stop button on the IE toolbar to close the document. |
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 is a number that indicates how far we want to move in the history list. A positive number means that we want 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.
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. It also has a toString method that 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>");
To understand how HTML objects work in JScript, 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 JScript HTML objects.
Our elementary HTML code is given in Listing 4.17. This code can
be found in simple4.htm on the
CD-ROM.
Listing 4.17 simple4.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:[email protected]"> <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 or her name. There is a Submit button for if he gets it right, and a Reset button for 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 [email protected].
The important aspect of this example is not its primitive HTML, but the fact that the HTML elements in it are reflected in the JScript 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 subobjects 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 JScript.