Copyright ©1996, Que Corporation. All rights reserved. No part of this book may be used or reproduced in any form or by any means, or stored in a database or retrieval system without prior written permission of the publisher except in the case of brief quotations embodied in critical articles and reviews. Making copies of any part of this book for any purpose other than your own personal use is a violation of United States copyright laws. For information, address Que Corporation, 201 West 103rd Street, Indianapolis, IN 46290 or at support@mcp.com.
Notice: This material is excerpted from Special Edition Using JavaScript, ISBN: 0-7897-0758-6. This material is provided "as is" without any warranty of any kind. Please see this DISCLAIMER.
by Mark Reynolds
There are many, many different computer programming languages in use today. Each has its own set of special features, which are highly praised by its fans and vigorously panned by its detractors. If you have worked in more than one language then you are well aware that there is a continuum of language styles, ranging from highly structured languages such as Ada to more free-wheeling ones such as Lisp. Some languages, such as HTML, the language used to describe the layout of World Wide Web pages, have a very well defined organizational structure, but very little in the way of traditional program structure (there are no data types, for example)
In trying to understand a new language it is not only important to master its syntax, it is also vital to appreciate its "style" - the way in which that language can be used to accomplish specific goals. We have already reviewed the basic goals of JavaScript in Chapter 1, as well as contrasting it to the more structured Java language. This chapter will describe the JavaScript language from both perspectives. A thorough description of its syntax will be given, and some initial concepts on how to structure a JavaScript program will also be introduced. Anyone who has programming in almost any modern declarative language, such as C, C++ or Pascal, will feel immediately at home. In addition, HTML authors who have never programmed will be able to rapidly acquire JavaScript proficiency.
This chapter will teach you the syntax and structure of the JavaScript language. You will learn how to do the following:
JavaScript is based on the action oriented model of the World Wide Web itself. Elements of a Web page, such as a button or checkbox, may trigger actions or events. When one of these events occurs a corresponding piece of JavaScript code, usually a JavaScript function is executed. That function, in turn, will be composed of various statements, which might perform calculations, examine of modify the contents of the Web page, or perform other tasks in order to respond in some way to that event. Pressing the SUBMIT button on an online order form might invoke a JavaScript function which validated the contents of that form to insure that the user had entered all the required information, for example.
In this section we will examine the syntax of JavaScript from the bottom up. We will begin with the most basic concepts of how to write a JavaScript statement, and what that statement does, and progress upward through more complex and powerful structures in subsequent sections, culminating in a detailed discussion of JavaScript functions and related concepts. Chapter 3 will return to the question of how these elements are tied into Web pages through events in much greater detail.
In general, the elements of a JavaScript program may be divided into five categories:
This is very similar to many other languages. As we examine each of these elements in subsequent sections we will discover that JavaScript is somewhat minimalist in its approach. Many familiar elements, such as explicit data types (int, String, REAL), are missing or have been substantially simplified. However, JavaScript also provides a number of powerful object oriented constructs which greatly simplify program organization. In this way JavaScript is both more than and less than languages such as C or Java.
One of the main differences between JavaScript and most other languages is that it does not have explicit data types. There is no way of specifying that a particular variable represents an integer, a string, or a floating point (real) number. Any JavaScript variable may be any of these - in fact, the same variable may be interpreted differently in different contexts.
All JavaScript variables are declared using the keyword var. A variable may be initialized, meaning that it is given a value when it is declared, or it may be unitialized. In addition, multiple variables may be declared on the same line by separating their names with commas. For example the statements
var x = 7 var y,z = "19" var lk = "lucky"
declare a variable named x with initial value 7, an unitialized variable y and variables named z and lk whose initial value are "19" and "lucky," respectively. It might seem that x is an integer, z and lk are strings and y is some undefined quantity. In fact, the real story is a little more complicated that this. The value of each variable depends on the context is which it is used. As you might guess the expressions
5 + x lk + z
will evaluate to 12 and "lucky19," seemingly confirming our suspicions about what they really are. However, it is also possible to form the expressions
lk + x x + z
which will evaluate to "lucky7" and 26, respectively. In the first expression x has been interpreted as a string, while in the second z has been interpreted as an integer.
These examples illustrate two critically important points about the JavaScript language. First, while JavaScript does not have explicit data types, it does have implicit data types. Second, JavaScript has a set of conversion rules which allow it to decide how to treat a value based on the context in which it is used. The context is establish by reading the expression from left to right. In the expression x + z, for example, x is implicitly a numerical value, so that JavaScript also attempts to view z as a number and perform the sum numerically. It succeeds, and the expected 26 results.
What would have happened if we had tried x + lk? The x variable occurs first on the left, and a number at heart. JavaScript thus tries to interpret the variable lk as a number, too. This in extremely unlucky, in fact, because "lucky" cannot be converted to a number (while z, the string "19" could). To understand JavaScript variables and values, therefore, it is necessary to understand its set of implicit types and how they may be converted to one another.
Before we enter into these details, let us consider one final example. In all the cases considered above the unitialized variable y was never used. What would be the value of an expression such as
x = z + y
Of course, as in all other programming languages, the result of using an uninitialized variable is never good. Since y has never been given a value there is no way in which this expression may be evaluated. It may result in something innocent, such as x be assigned the value of z, as is y were zero. It may also result in something much more serious, such as the value of x becoming something strange, or, more likely, a JavaScript error occurring. This leads to the following common sense rule.
Initialize all JavaScript variables to meaningful default values. If a variable has no meaningful default, initialize it to null.
There are five major implicit data types in JavaScript. A JavaScript value may be:
Actually, it would be more correct to say that there are five categories of data type, since it is possible to distinguish two different types of numbers (integers and floating point numbers), and many different types of JavaScript objects, functions and other structured types. In fact, Section 2 of those book is entirely devoted to explaining the many different JavaScript objects.
It is very important to distinguish between variables and their values. The statement x = 10 contains two components: the variable x and the literal value 10. A literal refers to anything which is referred to directly, by its actual value. A variable is just an abstraction which provides a way of giving names to values. Thus the statement x = 10 says "I am going to refer to the concrete (literal) quantity 10 by the abstract (variable) name x" just as you might say "I am going to call this lumpy thing I'm sitting on a chair". This also leads to the following important piece of advice.
It is bad practice to change the implicit data type of a variable. If a variable is initialized to have a certain type (such as string) is should always have that type.
Thus, since we have started out with x = 10 we should make sure that x always has some numeric value. There is no rule which prohibits us from later saying x = "Fortran," but this will generally lead to confusion or programming errors in most cases. No one will stop you from calling that lumpy thing you are sitting on "bacon and eggs" but many of your guests may become confused if you do so.
One final rule about variable names: a valid JavaScript variable name must begin with a letter or with the underscore character _. Case is important, so that norl, NoRl, NORL and _NORL are all valid JavaScript variable names which refer to different variables.
There are two numeric types in JavaScript: integers and floating point numbers. The rules for specifying both types are almost identical to those of C or C++ or Java. Integers may be specified in base 10 (decimal), base 8 (octal) or base 16 (hexadcimal) formats. The three forms are distinguished as follows, based on the first one or two characters:
Any of the three forms may also start with a + or - sign. Thus, -45 is a decimal integer, 017 is an octal integer and 0x12EF5 is a hexadecimal integer. The mimimum and maximum integers which may be used will be implementation dependent, but at least thirty two bits should be expected.
Floating point numbers may be specified in either the standard "." format or the engineering E-notation. Typical floating point numbers should contain a decimal point or an exponent, which may begin with either e or E. A floating point number may also have a + or - sign. 0.0, -1.4e12 and 3.14159 are all valid floating point numbers. The range of valid floats is again implementation dependent, but you should expect that any valid short floating point number, as defined by the IEEE standard will be acceptable.
Note that the original LiveScript language attempted to treat all the numeric types the same. Since it has become JavaScript there has been a convergence toward the numerical types of the Java language, and the distinction between integer values, such as 5, and floating point (or real) values, such as 3.3333 has increased.
Watch out for changes in the way JavaScript handles numeric types. In the future, the distinction between integers, single precision floating point types (floats) and double precision types (doubles) may become much sharper.
In JavaScript strings may be specified using either single quotes ('stuff') or double quotes ("otherstuff"). If you begin a string with one type of quote you must end it with that same form of quote, so that "badstuff' is not a legal string in JavaScript. Strings may also be nested by alternating the types of quotes used. Here is an example of several nested strings (with apologies to Rudyard Kipling):
"Oh, its 'Tommy this' and 'Tommy that' and 'throw im out, the brute'"
As in C and Java, JavaScript strings may contain special combinations of characters, known as escape sequences, to denote certain special characters. The rules for this are still emerging, but it is probably safe to assume that all the escape sequences defined in C will be supported. At the moment the following sequences are supported: \t is a tab, \r is a line feed, \n is a carriage return, \f is a form feed (vertical tab), and \b is a backspace. Since you will almost always be using formatting directives of HTML (such as <BR> for a line break) you will probably use these directives only rarely.
The special string "" or '' represents the zero length string. This is a perfectly valid string whose length is zero. This is the shortest JavaScript string; the length of the longest is, as usual, implementation dependent. It is reasonable to expect that most JavaScript environments will permit very long sonnets (or very short legislative measures) to be represented as single strings.
The logical, or Boolean, values true and false are typically used in expressions which test some condition to determine how to proceed. If that condition is met then one set of statements is executed, while if it is not then another set is used instead. The first corresponds to the true condition, while the second represents the false condition. Not surprisingly, such expressions are known as "conditional expressions."
As we shall see under "Operators and Expression" there are several comparison operators, such as the equality test ==, which result in logical values. Note that it is possible to think of true as 1 and false as 0, but you should avoid doing so as this can lead to type confusion.
The value null has a very special role in the JavaScript language. It is the value of last resort, so to speak, for every variable. For the beginning JavaScript programmer its primary role will in initializing variables which do not have any more meaningful initial value. In the set of variable declarations given above, for example, we should have actually written
var y = null
in order to initialize y to some value. This prevents JavaScript errors which arise when an uninitialized variable is accidentally used in an expression which requires a value. It is important to realize that the value null does not give the variable y any implicit data type. null also has the property that it may be converted to a benign form of all the other types. When it is converted to a number it becomes 0, when it is converted to a string it becomes the empty string "" and when it is converted to a Boolean value it becomes false. This is the one case where is it permissible to change the implicit data type of a variable after it is declared.
Therefore, statements such as
var lk2 = lk + y var w = x + y
will result in lk2 having the value "lucky" (the same as lk) and w having the value 10 (the same as x). This is why the value null is an excellent way of initializing variables - it is guaranteed to be harmless.
Several of the examples in the previous section used the "+" operator to combine different types of things. We saw that when a string was combined with a number in the form
stringthing + numberthing
the number was converted to a string and the "+" operator then glued the two string together (concatenation). However, if they were combined in the opposite order
numberthing + stringthing
then JavaScript would attempt to convert the stringthing to a number and add it, numerically, to numberthing. If the stringthing can be converted to a string, such as "-14," then all goes well. However, if it cannot then all goes poorly and an error results. This illustrates the concept of "implicit conversion" in JavaScript.
We have already seen that some examples of implicit conversion are completely safe. false may be converted to 0, "5" may be converted to 5, and null may be converted to just about anything. However, it is obvious that some conversions are invalid, while some might be called questionable. Questions such as "may the string '3.0' be legitimately converted to the integer 3?" are actually very difficult to answer with complete generality.
There are two approaches to handling this complex issue: use explicit conversion whenever possible, and use implicit conversion with great care. Both approaches should be used. A detailed study of explicit conversion will be taken up in Chapter 5, beginning with the section "The String Object." For the moment we will use the following rules of thumb.
Use implicit conversion only when converting to a string form. Never use it to convert to numerical form.
You have probably already noticed that conversion to a string is always safe, at least for the data types we have encountered so far. In fact, this type of implicit conversion is a boon to the JavaScript programmer, since it avoids the tedious formatting directives which are necessary in many languages such as C. In JavaScript we can say
"This page has been accessed " + cnt + " times today"
without having to worry about the data type of the variable cnt. This construction will always give a valid string, and never an error.
The second of our rules of thumb is also based on standard principles of defensive programming. There are many things which cannot be sensibly converted to a numerical form, so the prudent approach is to never try to implicitly convert anything to a number. There are several more robust approaches which can be used in case we have a string which we wish to try to convert to numerical form. These are described in Chapter 5. We will also see other exceptions to this rule as our mastery of JavaScript deepens.
The basic unit of work in JavaScript is the statement, as is the case in most programming languages. A JavaScript statement accomplishes work by causing something to be evaluated. This can be the result of giving a value to a variable, by calling a function, by performing some sort of calculation, or any combination of these three. We have already seen variable declaration statements, such as
var x = 10
which not only create (declare) a new variable, but also give it an initial value. JavaScript programs, as we learned at the beginning of this chapter, are collections of statements, typically organized into functions, which manipulate variables and the HTML environment in which the script itself works, in order to acheive some goal.
Before plunging into a detailed description of the various types of statements and the operators which they use, let's examine one simple statement in excruciating detail. Consider the statement
y = x + 5
This statement contains three parts: the result y, the operator = and the expression x + 5. The result always occurs in the left hand side, since JavaScript always operates from left to right, and is often called the lvalue. The result must always be something which may be modified. It would be erroneous to write null = x + 5, for example, because null is a built-in, unchangeable component of JavaScript itself - it cannot be modified, so it can never appear as a result.
The operator = is the assignment operator, of course. It causes the expression on the right to be evaluated and its value given (assigned) to the result. The expression x + 5 itself contains another operator, the + operator, which acts to combine x and 5 in some context-specific way. Since x is a number in this case the + operator will perform ordinary addition, and y will get the value 15. As we have already seen if x had been a string, such as "bleh," then + would have acted as a string concatenation operator and y would be given the value "bleh5" instead. This is an example of operator overloading: the + operator can do different things in different situations. Many JavaScript operators are overloaded.
There is one final point to be made about this statement, and about the structure of JavaScript programs in general. JavaScript has adopted a line oriented approach to program flow. This means that it knows that a statement has ended when it reaches the end of a line. It is also possible to explicitly terminate a statement with a semicolon ; character. The statement y = x + 5; is identical in effect to the statement shown above. This also means that you could, in fact, put multiple statements on a single line by separating each of them with a semicolon. However, defensive programming again motivates the following rule of thumb.
Terminate every JavaScript statement with a semicolon. Keep each statement on its own line.
This might seem both redundant and extraneous, but it is well justified. The "end of a line" is often a purely visual concept. Anyone who has every used a text editor has undoubtedly encountered the situation where a very long line looks like two lines. Different platforms (Macintosh, PC, Unix) also have their own unique ideas as to what the proper end of line character(s) are. It is much safer to put in the extra semicolon character and be explicit about the end of the statement than it is to rely on one's eyesight.
The set of operators which JavaScript uses is, once again, very similar to that of the C, C++ and Java languages. It provides a number of different ways of combining different values, both literals and variables, into expressions. Some operators require two elements to participate in the operation, and are referred to as binary operators. The + operator is a binary operator. Other operators require only a single participant (operand), and are known as unary operators. The ++ operator, which adds 1 to its operand, is a unary operator. Operators may also join forces to form aggregate operators, as we shall see below.
JavaScript operators may be classified into the following groups:
This grouping is purely functional, and is based on what the operators actually do. The next four subsections will examine each type of operator in more detail. Figure 2.1 summarizes the operators in each category and how they are used.
Fig 2.1
JavaScript operators can be used to perform many different tasks.
The computational operators are addition +, subtraction and negation -, division /, multiplication *, modulus %, increment ++, and decrement --. These operators are often used in performing arithmetic computations, but do not forget that the + operator is overloaded: it also has the extremely important role of string concatenation.
The first five computational operators have there standard mathematical meanings. They add, subtract, divide or multiply two numeric quantities. In combining two quantities using one of these operators the result is made as precise as possible. If an integer is added to a floating point number, the result will be a floating point number. The following four statements illustrate the use of these operators:
x = 4 + y; y = 5.5 - z; z = 10 / w; w = 1.4e5 * v;
Note that division of integer quantities will result in an integer result, so that if w had the value 4 in the third statement z would get the value 2, not 2.5. Note also that the - operator may also be used as a unary operator to compute the negative of a numeric quantity:
n = -m;
This has exactly the same effect as if we had multiplied m by -1.
The modulus operator % is used to compute the remainder from a division. Although it may be used with floating point numbers it is typically used with integers, so that 21 % 4 would evaluate to 1. The modulus operator always gives a remainder which is as close to zero as possible, so that -21 % 4 would evaluate to -1, not 3.
The increment and decrement operators and conveniences created to simplify the very common operations of adding or subtracting one from a number. Both these operators are unary and come in two forms: prefix and postfix. The expression ++x is the preincrement form of the ++ operator, while x++ is the postincrement form. This leads to subtle and often misunderstood point about the increment and decrement operators.
Suppose that x has it usual value 10, and consider the two statements
y = ++x; z = x++;
These look very similar, but are, in fact very different. After both these statements have been executed x will have the value 11. However, y will end up with the value 11 while z will have the value 10. Why? The reason has to do with the complex issue of what order the operators ++ and = are evaluated in these two statements. In the first statement the ++ is evaluated first, so that x attains the value 11, and then the assignment = is evaluated, so that this value is passed on to y. In the second statement the assignment operator = is applied first, so that z becomes 10, the current value of x, and then the ++ is applied to x, so that it advances to 11. The same rule applies to the decrement operator --.
This might seem like it is a violation of the rule of left-to-right evaluation, and it is. Even though the equal sign is to the left of the preincrement operator ++ in the first statement, the ++ operator takes effect first. This is an example of operator precedence, the order in which multiple operators are applied. This complex topic is discussed in more detail in the "Order of Evaluation" section below.
Logical operators in JavaScript are used either to carry out some form of test, or to combine the results of more than one such test. They are often referred to as conditional operators. The logical operators which perform a test of some sort are the (in)equality operator == and !=, the comparison operators <, <=, > and =>, and the logical negation operator !. The operators which combine logical values are logical AND && and logical OR ||. Finally, the conditional operator ? and the comma operator , and also combining operators, although not necessarily logical.
The binary equality == and inequality != operators are used to test if two quantities are the same, or different. These operators are overloaded. On integers they test for strict equality or inequality. On floating point numbers they test to see if the two quantities are equal within the precision of the underlying floating point type. On strings they test for exact equality - recall that case is significant in JavaScript strings. These operators all return a Boolean value, either true of false.
For example, if x has the value 10, y has the value 3.0 and z has the value "barney" then x == 10 is true, y != -5.0 is also true, and z == "fred" is false. Unfortunately, even operators as simple as these can be a source of error. It is regretable that the logical operator == looks so much like the assignment operator =. Consider the following incorrect code fragment:
if ( x = 3 ) { stuff..
The purpose of this code was almost certainly to test the value of the variable x against the constant 3, and execute the "stuff" if that test succeeded. This code fails to realize that purpose in two very dramatic ways, just by inappropriately using = instead of ==.
First of all, x = 3 will always give x the value 3, no matter what its previous value way. Instead of testing x using == we have altered it with =. Second, the value of the expression x = 3 is the value of its left hand side, namely 3. Even though 3 is not a true logical value, it will be treated as true by the if statement (if is described in greater detail under "Control Structure" below). This means that stuff will always be executed, rather than only being executed when x had the prior value 3.
This type of error occurs in every programming language in which similar operators are used for very different purposes. In this case we could have adopted another rule of defensive programming and said
if ( 3 = x ) { stuff..
In this case our typing mistake (= instead of ==) leads to an error, rather than resulting in a subtle programming flaw. Since 3 is a constant it can never appear on the left hand side of an assignment, but it is quite capable of appearing on the left hand side of a logical test. Said another way, since x == 3 and 3 == x are completely equivalent, the form 3 == x is preferable. If it is mistyped as an assignment statement ( 3 = x ) it will lead to an immediate error rather than one which might take hours of debugging to uncover. This leads to the following advice.
When testing for equality always put constants on the left, especially null.
There is another subtle evil about the (in)equality operators when they are used with floating point numbers. It is very tricky to make floating point arithmetic completely independent of the underlying machine. This means that z == 3.0 might be true on one machine but false another. It can also lead to seemingly absurd results such as 3. != 3.00 being false while 3.0 == 2.9999999 is true. A remedy for this problem will be presented at the end of this section.
The comparison operators <, <=, > and >= also operator on both numbers and strings. When they act on numbers they perform the usual arithmetic comparisons, yielding Boolean values as with the equality operators. When they act on strings they perform comparisons based on dictionary order, also known as lexicographic order. If a string str1 would occur earlier in the dictionary than a second string str2 then the comparison str1 < str2 (and also str1 <= str2 ) will be true. For example, "barney" < "fred" is true, while "Franklin" < "Delano" is false.
The logical negation operator ! is used to reverse the sense of a logical test. It converts true to false and false to true. If x < 15 is true then !(x < 15) is false, and vice versa. Note that ! may also be used with integer values, so that !0 is true, while !5 is false. As in other cases, this use of the ! operator violates type boundaries, and should be avoided.
The logical AND && and OR || operators are among the most powerful operators in JavaScript. Both may be used to combine two or more conditions into a composite test. The logical AND of a set of conditions is true only if all its component conditions are true. The logical OR of a set of conditions is true if any of its component conditions are true. Thus
( x < 17 ) && buttonPressed && ( z == "Meta" )
is true precisely when x is less than 17 and the Boolean variable buttonPressed is true and z is exactly equal to the string "Meta." Similarly
( x < 17 ) || buttonPressed || ( z == "Meta" )
will be true is one or more of the three conditions is true.
JavaScript's uses a lazy variant of its usual left-to-right evaluation rule with the && and || operators. This lazy evaluation (or short circuit evaluation) rule states that JavaScript stops trying to evaluate the expression as soon as its value is known.
To see how this works suppose that x has the value 20, buttonPressed is true and z is the string "Hyper". Since ( x < 17 ) is false the second and third conditions in the logical AND statement are never evaluated. This is because false && anything is always false, so the value of the first expression must be false. Similarly, the second statement stops as soon as buttonPressed is evaluated. Since true || anything is always true, the second expression must be true.
Lazy evaluation can be both a boon and a curse. Suppose that "digofpi(1000000000)" is a function which computes the billionth digit of pi. The expression
( x < 25 ) || ( digofpi(1000000000) == 3 )
will not actually try to compute the billionth digit of pi if x is 20, because the expression is already known to be true, and digofpi() is never called. By the same token, if beaupage() is a function which displays a beautiful Web page and then returns true, that page will never be seen if the expression
( x < 25 ) && beaupage()
is evaluated, and x is 30, because the expression is already known to be false and beaupage() is never called. We will revisit this phenomenon in the "Functions and Objects" section at the end of this chapter. For the moment is it wise to be aware of lazy evaluation.
The logical AND and OR operators also provide us with one solution to the problem of floating point comparison. While it may not be possible to ever determine if x is exactly equal to the constant 3.0 we can be certain that it is close using a combined test such as
( x - 3.0 ) < epsilon || ( 3.0 - x ) < epsilon
where epsilon is some suitably small value, such as 0.001. This form of test is often referred to as a fuzzy comparison.
Floating point arithmetic is not an exact science. Avoid exact comparison test such as == and !=. Use "fuzzy" comparisons instead.
The final two operators in the logical category are the conditional operator ?, often called the question mark operator, and the comma operator ,. These two operators are only vaguely logical, but they don't readily fall into any of the other categories either.
The conditional operator is the only trinary (3 operand) operator in JavaScript. It is used to select one of two possible alternatively based on a conditional test. The syntax for this operator is
( conditionthing ? truealt : falsealt )
If the conditionthing is true then the value of this expression is truealt; otherwise it is falsealt. Note that the colon : separating the true alternative from the false alternative is mandatory. This can be used to select an appropriate alternative and simplify code, as in this example:
printme = ( errorcode == 0 ? "OK" : "error" );
This expression makes the variable printme have the string value "OK" in case the variable errorcode is 0; otherwise, it will be set to "error." The question mark operator is often a fast way to select one of two choices when a control structure would be unnecessarily cumbersome.
Finally, the lowly comma operator can be used to force the evaluation of a set of expressions. All intermediate results are discarded, and the value of the very last expression on the right is is returned. For example the expression
b = (d = digofpi(1000000000)), beaupage(), (x < 17);
will always compute the billionth digit of pi and assign it to the variable d, will always display the beautiful page, will always compare x against 17, and will only then return the result of that comparison, since x < 17 is the rightmost expression. The result of that comparison will be assigned to the Boolean variable b. This might seem like a clever way to outwit JavaScript's lazy evaluation, but would it not be clearer to simply write
d = digofpi(1000000000); beaupage(); b = ( x < 17 );
In general the comma operator is only useful when it is inside a for loop (see the section on "Control Structures"), and should otherwise be ignored.
In many situations you do not need to know, nor do you wish to know, the precise binary representation of values in your program. There are some situations, however, it which it is absolute essential to operate at the lowest possible level and deal with the individual bits of a particular value. This often arises in mathematical applications, or when precisely manipulating color values, for example. The bitwise operators are used for this purpose.
The bitwise operators in JavaScript are bitwise AND &, bitwise OR |, bitwise xor ^, bitwise left shift <<, bitwise signed right shift >>, and bitwise unsigned right shift >>>, and bitwise not ~. These are all binary operators, except bitwise not, which is unary. Each operators on its operands one bit at a time.
Bitwise AND & examines each bit position in each of its operands. If both operands have a 1 bit in a given position, then that bit will also be set to 1 in the result. In all other cases, the output bit position will be zero. For example, support x = 0x00001234 and y = 0x8000ABCD. Then z = x & y will have the value 0x00000204. We can see this more easily by writing x and y in base 2 (binary) notation, and looking for those positions in which both x and y are 1:
x 0000 0000 0000 0000 0001 0010 0011 0100 y 1000 0000 0000 0000 1010 1011 1100 1101 z 0000 0000 0000 0000 0000 0010 0000 0100
Note that x and y only have the same bits set in underlined positions, so that those are the only bits set in their logical AND z. In this way bitwise AND is the bit level analogue of the logical AND. Bitwise OR | is similar. If either bit is 1 in any bit position, then that bit will be 1 in the result. Thus the value of w = x | y will be 0x8000BBFD, as we see from the following tableau
x 0000 0000 0000 0000 0001 0010 0011 0100 y 1000 0000 0000 0000 1010 1011 1100 1101 w 1000 0000 0000 0000 1011 1011 1111 1101
Each bit is set in w if either or both of the corresponding bits in x and y is set. The bitwise xor (exclusive OR) ^ operator is a variation on the bitwise OR operator. It sets a bit in the result if either bit in the operand is set, but not both. The value of v = x ^ y is 0x8000B9F9, as follows:
x 0000 0000 0000 0000 0001 0010 0011 0100 y 1000 0000 0000 0000 1010 1011 1100 1101 v 1000 0000 0000 0000 1011 1001 1111 1001
These three operators may also take more than two operands, so that is it possible to write a very long expression such as
n = ( a & b & c & d & e );
which operates from left to right, as usual. This expression takes the bitwise AND of a and b, AND the result of that with c, ANDs that with d, and finally ANDs that with e. The final result is saved in the variable n.
The bitwise AND & and OR | operators bear a shocking similarity to their logical counterparts && and ||. This can lead to painfully undetectable errors. The same care which is exercised with = and == should also be used with these operators.
The unary bitwise NOT operator ~ changes each 0 bit in its operand to a 1 bit, and each 1 bit in its operand to a zero bit. The bitwise NOT of x will have the value 0xFFFFEDCB:
x 0000 0000 0000 0000 0001 0010 0011 0100 ~x 1111 1111 1111 1111 1110 1101 1100 1011
While &, |, ^ and ~ operate on bits in place, the shift operators <<, >> and >>> are used to move bits around. The left shift operator << will shift a set of bits to the left by a specified number of positions, while both >> and >>> will move that set of bits to the right in two potentially different ways. For example, let us evaluate these three expressions:
xleft = x << 5; ysright = y >> 3; yusright = y >>> 3;
The first of these shifts each bit in x to the left five positions. Zero bits are tacked on at the right, while the bits which are shifted out at the left are lost when the exceed the overall thirty two bit length. So the value of xleft must be 0x00024680. The signed right shift operator acts in almost the same way. Each bit of y is shifted to the right 3 positions. Bits are the right edge of y are lost as they are shifted out. However, rather than shifting in zeros at the left side of y, the most significant bit of y, which happens to be 1 in this case, is shifted in. The resulting value of ysright is 0xF0001579.
This might seem counterintuitive, but is makes good mathematical sense, since it preserves the sign of the operand. If y is negative (most significant bit set, as in our example) then any signed right shifted version of y will also be negative. Similarly, if y had been positive (most significant bit equal to 0) then any right shifted version of y would have been positive. The unsigned right shift operator >>> does not preserve the sign of its operand; it always shifts 0 bits in at the left edge. The value of yusright is therefore 0x10001579. Here are the resulting bit patterns:
xleft 0000 0000 0000 0010 0100 0110 1000 0000 ysright 1111 0000 0000 0000 0001 0101 0111 1001 yusright 0001 0000 0000 0000 0001 0101 0111 1001
Since all the bitwise operators act at the bit level, chaos can result if they are applied to a variable which is not an integer. Floating point numbers are particularly sensitive, since an arbitrary bit pattern need not correspond to a valid floating point number.
Never perform bitwise operations on floating point numbers. Your code will be unportable, and floating point exceptions may result.
Our tour of JavaScript operators concludes with the assignment operator and its aggregates. You have already seen many examples of that most fundamental of all operators, the assignment operator =. You are well aware that it is used to assign the result of an expression or value on the right hand side of the = sign to the variable or lvalue on the left hand side of the = sign.
In JavaScript, as in C, C++ and Java, you can also combine the assignment operator with any of the binary computational and logical operators. The expression
Left OP= Right ;
is just a shorthand for the expression
Left = Left OP Right ;
where OP is any of the operators +, -, /, *, %, &, |, ^, <<, >> or >>>. So, to add 7 to x, multiply y by 19.5, OR z with 0xAA7700, and perform an unsigned right shift of 10 bits on w we can write
x += 7; y *= 19.5; z |= 0xAA7700; w >>>= 10;
as well as using the wordier versions x = x + 7; and so forth.
In elementary school math you were probably confronted with questions such as "what is the value of 3 + 4 * 5? Is it 23 or is it 35?" This was your first exposure to the concept of order of evaluation, or operator precedence. You probably remember that multiplication has a higher precedence that addition, so that the correct answer is 23. The same issue arises in almost every programming language with the concept of "operators" - which comes first.
There are two approaches to this issue. The first involves learning, or attempting to learn, the operator precedence table. The more operators there are, the more rules there must be in this table. The second approach is to simply ignore the issue completely and explicit group your expressions using parentheses. Never write 3 + 4 * 5. Always write 3 + ( 4 * 5 ) or even ( 3 + 4 ) * 5 if that is what you want.
This recommendation is very much like several others in this chapter. It trades the effort (and perhaps some readability) of using the explicit parenthesized form, against the promise that the order of evaluation will always be exactly as you wrote it. Incorrect order of evaluation is almost certainly the second most common source of programming error (confusing = and == is the first). For the daring, figure 2.2 shows the operator precedence table for JavaScript. For everyone else the following rule of thumb is recommended.
Fig 2.2
Use the operator precedence table to determine the order of evaluation.
Use parentheses to explicit specify the order of evaluation in expressions containing more than one operator.
There is one case in which no amount of parentheses will help. When using the increment ++ and decrement -- unary operators you must simply know that preincrements and predecrements always happen before anything else.
All professional code should have comments which clearly indicate the purpose and logic behing each major section of the code. JavaScript offers two comment styles - the original comment style from C and the one line comment style from C++ and Java.
C style comments are typically used to document major functions or code blocks. Because a C comment may extend over multiple lines it is ideal for detailed discussions of important parts of the code. A C comment begin with /* and end with */. Our aesthetically pleasing function beaupage() might begin with a thorough description of just what makes it so beautiful, as shown below
/* The function beaupage() draws a stunningly beautiful Web page by performing the following nineteen steps. ... list of the 19 steps */
By contrast C++ style comments are most suitable for short, pithy descriptions which will fit on a single line. A C++ style comment begins with // and ends at the end of the current line. Critical variables, for example, might merit a Java style comment indicating how they will be used, as shown below
var done = false; // set to true when we are all done
Both comment styles may be mixed freely in the same JavaScript program. However, such comments should never be nested, as this can lead to confusion. Also, the temptation to use HTML style comments should be strongly resisted, for reasons which will become clear in the next chapter.
See Chapter 3, "The SCRIPT Tag"
Troubleshooting
I have just written my first JavaScript program. Everything looks just fine, but the code does nothing. What is wrong? Here is the code:
/* My first JavaScript program *? ... many lines of code not shown /* End of my first JavaScript program */
Your comments are well thought out and informative. Unfortunately, your very first comment begins with /* but does not end with */. You have inadvertantly typed *? instead of */, so that the comment does not end until very far down in your program. When you use C style comments always make sure that they match.
At this point we have had just enough of the JavaScript language to declare variables, perform assignments and do various types of arithmetic, string and logical calculations. We are not yet able to write any meaningful code because we do not have any higher level constructions. In this section we will consider various methods of controlling the way in which statements are executed. The next section will expose the highest level of JavaScript - its functions and objects.
There are three type of control structure in JavaScript:
These three control structures are very similar. Each is introduced by a keyword (while, for and if, respectively) and each manipulates a block of JavaScript statements. A block is introduces by a left brace { and terminated with a right brace }. There can be as many JavaScript statements between { and } as you wish, or as few. A block of code may even be empty, with nothing between the braces. In many ways a block of statements is like a single gigantic statement. In particular, block structured constructs are often all or nothing: either the entire contents of the block are executed, or none of it is. Since blocks behave like single statements, it is also possible to put blocks inside other blocks, in a nested fashion.
As we will see, each of the three control structures has its own specific format and its own special uses, although it is often possible to acheive the same results using any of the three types, with varying degrees of elegance.
The if statement is used to conditionally execute a single block of code. It has two forms, the simple if statement and the if..else statement. The simple if statement consists of a conditional expression, known as the "if test" and a block of code which is executed if that expression evaluates to a Boolean true:
if ( condstmt ) { zero or more statements }
The block of code within the braces is often called the "if block." The conditional statement condstmt can be any expression which yields a logical value. Note that numerical expressions may also be used; 0 is construed as false and all other values are taken to be true. As stated above, an if statement should be considered a single statement. Code blocks are not traditionally terminated with a semicolon, although there is no harm in doing so. Listing 2.1 shows an example of a simple if statement.
Listing 2.1 The if Control Structure
if ( ( x < 10 ) && ( -10 < x ) ) { // if test y = ( x * x * x ); // statement 1: cube of x ystr = "The cube of " + x + " is " + y; // statement 2: informative string }
In this example the value of x is tested to see if it is less than 10 and also greater than -10. If the result of this test is true then the variable y is set equal to the expression x * x * x, known mathemtically as the cube of x, in the statement labelled "statement 1". The variable ystr is then set of a string which expresses this cubic relationship between x and y, in statement 2. If x fails either of the two tests in the if test then neither of the two statements in the if block will be executed.
It is easy to see even in this simple example that it is often desireable to have a contingency plan in case the if test is false. This leads to the second form of the if statement, the if..else control structure. In this form one block of code is executed when the if test passes, and a second block is executed in case it fails. The format of this type of if statement this:
if ( condstmt ) { ifblock of statements } else { elseblock of statements }
In the current version of JavaScript the placement of the braces is critical. The opening { brace must be on the same line as the if keyword. If an else clause is present the closing } brace of the if and the opening { brace of the else must both be on the same line as the else keyword itself.
In this form of the if statement the if block is still executed if condstmt is true. However, in this case the block of code following the else will be executed if condstmt is false. Listing 2.2 shows an enhanced version of the code from Listing 2.1 using the if..else form.
Listing 2.2 The if..else Control Structure
if ( ( x < 10 ) && ( -10 < x ) ) { // if test y = ( x * x * x ); // 1: cube of x ystr = "The cube of " + x + " is " + y; // 2: informative string } else { // false case y = null; // 3: be paranoid; give y a value ystr = "Cannot compute the cube of " + x; // 4: explain the failure }
In this example statements 1 and 2 are still executed if x meets both tests in the if test. If either test fails then statements 3 and 4 in the else block will be executed instead. Statement 3 is another example of defensive programming. The variable y is given a value, albeit a meaningless value. This is done so that if y is used later it will be guaranteed to have some value (even if we forgot to initialize) regardless of whether the code flowed through the true part of the if (the if block) or the false part of the if (the else block).
Observe that ystr also gets a value no matter which of the two blocks is used. In the true case it has the informative string documenting the cube of x; in the false case it has a string indicating that the cube of x could not be computed. Since ystr will presumably be displayed to the user at some point it is worthwhile to provide an error message. This is an example of parallel code design. Each conditional path modifies the same set of variables. For a simple example such as this it is easy to insure that this happens; we can see exactly which variables are set in every case. Formore complicated, nested conditional expressions this can become almost impossible. It is a good goal to strive for nonetheless.
The while statement is used to execute a block of code while a certain condition is true. The format of the while statement is
while ( condstmt ) { zero of more statements }
The condition clause condstmt is evaluated as a logical expression. If it is true then the block of statements between the braces is executed. The flow of control then loops back to the top of the while statement, and condstmt is evaluated again. This process continues until the condstmt becomes false, or until some statement within the block forces it to terminate. Each pass through the block of code is called an "iteration".
The first fundamental difference between a while statement and an if statement is that the while block may be executed many times, while the if or else blocks are executed at most once. You might well wonder how a while statement ever terminates. The code shown in Listing 2.3 illustrates a simple situation in which the while block itself eventually leads to the condstmt becoming false.
Listing 2.3 A while loop which adds a sequence of numbers
var x = 1; var xsum = 0; while ( x <= 10 ) { // loop until x is greater than 10 xsum += x; // add x to the running sum xsum x++; // increment x }
This code accumulates the sum of all the integers between 1 and 10, inclusive, in a variable called xsum. x starts out as 1, so that xsum initially becomes 1 as well. x is then incremented to 2 by the x++ statement. That value is then added to xsum, so that it becomes 1 + 2 = 3. This process continues until x finally becomes 11 and the x <= 10 condition is false. xsum at this point has the value 1 + 2 + ... + 9 + 10 = 55. Thus, the loop terminates. Note that it is critically important to initialize xsum to 0. If xsum is not initialized at all then the statement xsum += x which is just shorthand for xsum = xsum + x, will give an error. If xsum is initialized to something other than zero the final result will contain that initial value, and will not be just the sum of the integers from 1 through 10.
Listing 2.3 shows one way in which a while loop may terminate. Statements within the block may cause the conditional statement to become false. It could also happen that the conditional statement at the top of the while was never true, so that the statements within the block are not executed even once. If x had started with the value 20 in this example then the while test would have been immediately false, and the statements xsum += x and x++ would never be executed. In this event xsum would retain its initial value of zero.
There is a third way for a while loop to terminate. If the special statement break is encountered inside the while block this forces the loop to terminate immediately. No further statements are executed and the condstmt is not retested. Execution continues with the first statement after the end of the while block. Listing 2.4 gives an example of the use of the break statement.
Listing 2.4 A while loop with an internal break statement
var x = 1; var xoddsum = 0; var xtmp = 0; var lastx = 0; while ( true ) { // 1: loop forever (well, almost) xtmp = xoddsum + x; // 2: compute a trial sum if ( xtmp > 100 ) // 3: if it is too large, then... break; // 4: we are done xoddsum += x; // 5: add x to the running sum xoddsum x += 2; // 6: increment x by 2 } lastx = x; // 7: save the final value of x in the variable lastx
The test clause of this while (statement 1) is true, which, one might well suspect, is always true. This means that there is no way for this loop to terminate unless it is forced to do so by a break statement. In statement 2 a temporary sum is formed in the variable xtmp. This sum is tested against the limit 100 in statement 3; if xtmp exceeds it then statement 4, the break statement is executed, and the loop terminates. If the test fails (xtmp is still less than one hundred) then the real sum is formed in statement 5. (Note that it would have been completely equivalent, and slightly more efficient, if we had written statement 5 as xoddsum = xtmp.) In statement six x is incremented by 2.
What does this while loop do? It keeps adding up numbers, odd numbers in fact, until the sum is less than 100. When the next sum would have exceeded 100 the if test succeeds, the break is executed, and the flow of control of the program reaches the first statement after the entire while block, namely statement 7. This statement saves the last value of x in a different variable, lastx. So this construction computes the largest sequence of odd numbers which can be added without having the sum exceed 100. You can easily determine for yourself that the value of lastx must be 21, since 1 + 3 + .. + 21 = 100 exactly, while 1 + 3 + .. + 21 + 23 = 123 > 100.
This example not only illustrates the use of break, it also shows two other elements worth noting. First, Listing 2.4 contains a nested conditional: there is an if statement inside the while block. This sort of construct is extremely common, and many levels of nesting are not at all unusual. Second, this example has another very common but somewhat troublesome feature. Since the while test is always true, there is no way for the while to terminate unless the break statement is executed. In our case it was, quite quickly. Suppose, however that statement 6 had been incorrectly entered as x -= 2. In this case xoddsum would be getting constantly smaller and xtmp would never exceed 100. This type of error is known as an infinite loop. Listing 2.3 is not immune either, even though it has a conditional test rather than a blanket true. If the final statement of that example had been mistyped as x-- it would never terminate either.
Naturally, infinite loops must be vigorously avoided. They will only terminate when some kind of internal error happens (such as an arithmetic overflow when something becomes too large) or as a result of user intervention. Unfortunately, there is no foolproof way to write a while statement (or a for statement, as we shall see shortly) which is guaranteed to be correct. JavaScript is no different than any other programming language in this respect. The following general principles will reduce the opportunity for error, however.
Avoid "while ( true )" whenever possible
Have at least one way of exiting the loop body
The first suggestion arises because "while ( true )" is asking for trouble. This forces the logic which exercises the break statement to be correct. If it isn't then the loop will never terminate. The second suggestion is based on the observation that the more chances there are to exit the loop, the less likely it is that the loop will last forever. Listing 2.5 shows a modified version of Listing 2.4 in which we have moved the test on the sum to the while clause itself, and have also added a very paranoid test on the number of times through the loop (the variable loopcount).
Listing 2.5 An improved form of Listing 2.4
var x = 1; var xoddsum = 0; var lastx = 0; var loopcount = 0; while ( ( xoddsum + x ) < 100 ) { // 1: loop while sum is < 100 xoddsum += x; // 2: add x to the sum xoddsum x += 2; // 3: increment x by 2 if ( ++loopcount > 1000 ) // 4: if we're working too late.. break; // 5: quit } lastx = x; // 6: save the final value of x in lastx
This version satisfies both rules. Of course, the test in statement 4 is completely unnecessary. The code is simple enough that we can reassure ourselves that it is correct, and will not go into an infinite loop. Once you are writing very slightly more complicated while loops you will find that there are usually multiple possible error conditions which might arise. Each of these is an excellent time to use a break statement.
You will often see while ( true ) written as while ( 1 ). These are equivalent, since true has the numerical value 1, but the latter form is sloppy. The conditional portion of a while, if or for statement should always be a true logical expression.
There is another special statement which may be used inside while loops: the continue statement. The continue statement is used to force the flow of control back to the top of the while loop. When a continue statement is seen all statement between it and the end of the while block are skipped, and execution continues at the top of the while. Listing 2.6 shows a simple use for the continue statement.
Listing 2.6 A continue statement returns to the top of a while
var x = 0; var xsum = 0; var loopcount = 0; while ( loopcount++ < 100 ) { // 1: loop 100 times x++; // 2: increment x if ( ( x % 5 ) == 0 ) // 3: if x is divisible by 5 continue; // 4: skip it xsum += x; // 5: otherwise, add x to xsum }
This example adds up every number between 1 and 100 which is not divisible by 5. The numbers which are divisible by five are skipped by virtue of statements 3 and 4. Statement 3 computes the remainder when x is divided by 5. If that remainder is zero then x must be evenly divisible by 5. In that case the conditional in statement 3 is true and statement 4 is executed. The continue statement causes execute to continue back to the the top of the loop at statement 1. This means that statement 5 is not executed, so that the sum always misses value of x which are divisible by 5, and only those values.
One striking difference between this Listing and the previous three is that x is initialized to 0, not 1, and x is incremented at the top of the loop, not at the bottom. If the x++ were at the bottom, what would happen? The values 1, 2, 3, and 4 would all be gleefully added into xsum. When x reached 5, however, statement 3 would be true, the continue in statement 4 would be executed, and both xsum += x and x++ would be skipped. x would stay equal to 5 forever! Since the x++ statement is critical to the correct functioning of the loop, it must occur before the continue. If it occurs after the continue it will be skipped.
Any statement which must be executed on every pass through a loop must be placed before any continue statements.
The for statement is the most powerful and complex of the three flow control constructions in JavaScript. The primary purpose of the for statement is to iterate over a block of statements for some particular range of values. The for statement has the following format
for ( initstmt; condstmt; updstmt ) { forblock }
The for clause, as shown, has three parts, separated by two mandatory semicolons. The initstmt is typically used to initialize a variable, although any valid statement may be used in this position. The initstmt is always executed exactly once, when the for statement is first encountered. The condstmt is a conditional test, and serves exactly the same function as in the while statement. It is tested at the top of each loop. The for statement terminates when this condition evaluates to false. The updstmt is executed at the bottom of each loop. It is typically used to update the variable which is initialized by the initstmt.
Listing 2.7 shows a simple example of a for statement. In fact, the code in this listing accomplishes exactly the same task as the code in Listing 2.3. Note that this code does not bother to initialize x when it is declared. This is because the initstmt part of the for loop sets it equal to 1 immediately.
Listing 2.7 Adding up a sequence of numbers using for
var xsum = 0; var x; for ( x = 1; x <= 10; x++ ) { // 1: loop while x is <= 10 xsum += x; // 2: add x to xsum }
In many ways the for statement is very much like a fancy version of the while statement. Many of the observations which were made for while also hold true for the for statement. In particular, it is possible to use the break and continue statements within a for loop. One of the advantages of a for loop is that its update statement is executed on every pass through the loop, even those passes which are cut short by a continue. The continue skips every statement in the block, but it does not cause the update statement to be skipped. The for statement may also be used unwisely, just like the while statement. If the condstmt portion of the for clause is omitted it is as if a true conditional had been used, so that something while the block of statement which makes up the body of the for must force looping to terminate. You will occasionally see the construction for(;;), which is identical in meaning to while ( true ). The two semicolons are mandatory.
The for statement also has some unique features which are not shared by while. The first is that variables may actually be declared and initialized within the initstmt portion. In Listing 2.7 we could have dispensed with the external declaration of x, and put var x = 1; as the initialization portion of the for loop. This is often very convenient, since the loop variable (x in this case) if often used only within the loop itself, so that an external declaration is often pointless.
A second useful feature of the for statement is that both the initialization portion and the update portion of the for clause may contain multiple statement separated by the comma , operator. Listing 2.8 shows another version of the code in Listing 2.6, rewritten so that both x and lcnt become loop variables.
Listing 2.8 A for loop with multiple initialization and update statements
var xsum = 0; for ( var x = 1, lcnt = 0; lcnt < 100; x++, lcnt++ ) { if ( ( x % 5 ) == 0 ) // if x is divisible by 5 continue; // skip it xsum += x; // otherwise, add x to xsum }
This usage underlines the fact that both x and lcnt are used only within the body of the for loop. It is also much more compact than its counterpart in Listing 2.6. In this example we needn't worry about the logical effect of the continue; we know that both x++ and lcnt++ will always be executed. This usage is also the most common place where the comma operator is really useful.
Finally, there is another form of the for statement which is used exclusively with objects and arrays in JavaScript, the for..in statement. We will see how this is used in Chapter 4. Figure 2.3 shows the basic structure of the for, while and if statements, and the use of the break and continue statements within them.
See Chapter 4, "Objects, Methods and Properties in JavaScript"
Fig 2.3
Control statements determine the flow of execution in JavaScript
The basic statements, expressions and operators which were discussed at the beginning of this chapter are what computer scientists usually call primitives. Primatives are the building blocks from which more complex elements of a program may be constructed. The for, while and if control structures represent the next higher level of organization in JavaScript. Each of these statements deals with blocks of code whose execution is controlled by the various conditional and other clauses which characterize these statements.
Functions and objects represent the highest level of organization within the JavaScript language. We will spend many chapters learning how to make effective use of these concepts. The purpose of this section is to introduce them and describe their basic features.
A function is an block of code which has a name. Whenever that name is used the function is called, which means that the code within that function is executed. Functions may also be called with values, known as parameters, which may be used inside the body of the function. Functions serve two purposes. A function is an organizational tool, in the sense that it permits you to perform the same operation without simply copying the same code.
Actions on a Web page may also be linked directly with JavaScript function. Mouse clicks, button presses, text selections and other user actions can call JavaScript function by including suitable tags in the HTML source for the page.
The syntax for a function statement in JavaScript is
function Name ( listofparams ) { body }
The function's Name is given immediately after the function keyword. All function names should be unique, and should also not conflict with any of the statement names which JavaScript itself uses (known as the reserved words). You cannot have a function named while, for example, and you should not have two functions both named UserHelp. The listofparams is a comma separated list of the values which are passed into the function. These are referred to as the functions parameters or arguments. This list may be empty, including that the function does not use any arguments (often called a void function). The function's body is the set of statement which make up the function. Listing 2.9 shows a function which will add up all the integers starting at 1 and ending at a value given as the sole argument.
Listing 2.9 A summation function
function summation ( endval ) { var thesum = 0; // this variable will hold the sum for ( var iter = 1; iter < endval; iter++ ) { thesum += iter; // add the integer into the sum } // end of the for loop return( thesum ); // return the sum }
This function does the same task which came up in the discussions of the while and for statements earlier in this chapter. Now that it has been written as a function this code never needs to be repeated again. Anytime we wish to form the sum 1 + 2 + ... + N we can simply call the function, as summation(N) and it will perform the task. Notice that the endval parameter is used as the argument to the function.
When the function is called, as summation(14) for example, the actual value 14 is used for endval within the function. The function then executes the for statement, with iter < 14 as its termination condition, adding in each successive value into the variable thesum. When the for loop is done the function executes the return statement. This causes the function to give the value inside the return statement back to the caller. This means that if we write
var sum14; sum14 = summation(14);
the variable sum14 will be set to the value returned by the summation function when endval is given the value 14, namely 105. Functions may return any type of value, and are not restricted to returning integers.
There are several things to notice about this example. First of all, the variables thesum and iter, which are declared within the body of this function, are local variables. This means that they are only known within the body of this function, and are therefore completely unknown outside it. It is quite possible, even likely, that there are many functions all of which have a local variable named iter. All these various iters are unrelated. Changing the value of one of these iters would not effect any of the others. This is why the return statement is necessary; it is the only way to communicate the work of the function back to the caller.
This same restriction applies to the parameter endval as well. The arguments to a function may not be changed within that function. We could well have written endval = 15 just before the return statement in Listing 2.9. This statement would do nothing; it certainly would not change the caller's 14 into a 15. It might seem like every function would always have a return statement. This is not the case, however, since it is possible for a function to have side effects without aually returning a value. This happens by referencing external objects, which are our next topic.
Functions are used to provide an uniform method for organizing code. Objects serve the same purpose for data. Up to this point the only data items which we have seen are simple variables declared with var. Each of these typeless quantities can only hold a single value of some sort at a time. Objects provide the ability to hold multiple values, so that a group of related data elements may be associated with one another.
What JavaScript calls an object would be called a data structure (or class) in many other languages. As with JavaScript functions there are two aspects to JavaScript objects: creating them and using them. For the moment we will defer the question of how to create objects and concentrate on how they are used. We will also see that a JavaScript capable browser will provided a number of its own, built-in objects.
A JavaScript object is made up of a set of component parts, which are called its properties or members. Suppose we have an object named appt which we are using to organize our appointments. The appointment object might have properties which specify the date and time of the appointment, as well as the name of person with whom the appointment will take place. It might also have a general description field to remind us of the purpose of this meeting. Thus, we can imagine that the appt object will have the following properties:
Each of the properties of the appt object are referenced using the dot . operator. Thus appt.month refers to the month property and appt.why gives us the reason for the appointment. These references may appear on both the right and left hand sides of an expression; they may be set and gotten. Listing 2.10 shows a code fragment which would test the value of appt and display a message about a current appointment.
Listing 2.10 Using the appointment object
if ( appt.day == Today ) { document.write('<BR>You have an appointment today<BR>'); document.write('See ' + appt.who + ' at ' + appt.time<BR>'); document.write(appt.why + '<BR>'); }
This example assumes that the variable Today has somehow been initialized with today's date, so that the equality test with appt.day will only be true for today's appointments. If the test does succeed then the three statements in the if block will be executed. Each of these references document.write. The document object is a built-in object of the Netscape Navigator browser. This object has a member known as write, which is actually a function. Functional members of JavaScript objects are known as methods. This particular method takes a string and displays it on the current Web page.
Each of the three strings which are passed to document.write are constructed using + as a string concatenation operator. Each of them references one or more properties of the appt object in order to provide meaningful messages to the user. Each also includes <BR>, the HTML construction for a line break. This ability to directly issue HTML directives is one of the most powerful aspects of JavaScript, as it allows the programmer to dynamically modify the contents of Web pages using JavaScript functions and objects.
Once we have learned more about the Date object, in "The Date Object" section of Chapter 5, we will be able to construct a much more satisfying version of this example. Even at this stage, however, the advantage of object based programming should be apparent. Rather than carrying about many variables we can use objects instead. Each object can contain all the variables of interest to a particular idea. It can also contain method functions which perform related work. Objects can even contain other objects, so that we may organize our data in a hierarchical structure. Subsequent chapters will explore these ideas in much greater detail.
|