Once you start to write more complex programs, you will quickly find the need to perform some tasks and actions more than once during the course of a program.
This need is addressed by functions, which are similar to methods but are not attached to any particular object. As a programmer, you can create numerous functions in their programsthis helps organize the structure of their applications and makes
maintaining and changing their program code easier.
In addition, functions are particularly useful in working with events and event handlers as you will learn in Chapter 5, "Events in JavaScript".
You can also use functions as the basis for creating their own objects to supplement those available to them in JavaScript.
In this chapter we will cover these topics:
Functions offer the ability for programmers to group together program code that performs a specific taskor function into a single unit that can be used repeatedly throughout a program.
Like the methods you have seen in earlier chapters, a function is defined by name and is invoked by using its name.
Also, like some of the methods you have seen before (such as prompt() and confirm()), functions can accept information in the form of arguments and can return results.
JavaScript includes several built-in functions as well as methods of base objects. You have already seen these when you used alert(), document.write(), parseInt() or any of the other methods and
functions you have been working with. The flexibility of JavaScript, though, lies in the ability for programmers to create their own functions to supplement those available in the JavaScript specification.
In order to make use of functions, you need to know how to define them, pass arguments to them, and return the results of their calculations. It is also important to understand the concept of variable scope, which governs whether a variable is available
in an entire script or just within a specific function.
Functions are defined using the function statement. The function statement requires a name for the function, a list of parametersor
arguments that will be passed to the function, and a command block that defines what the function does:
function function_name(parameters, arguments) { command block }
As you will notice, the naming of functions follows basically the same rules as variables: They are case sensitive, can include underscores (_)
and start with a letter. The list of arguments passed to the function appears in parentheses and is separated by commas.
It is important to realize that defining a function does not execute the commands that make up the function. It is only when the function is called by name somewhere else in the script that the function is executed.
In the following function, you can see that printName() accepts one argument called name:
function printName(name) { document.write("<HR>Your Name is <B><I>"); document.write(name); document.write("</B></I><HR>"); }
Within the function, references to name refer to the value passed to the function.
There are several points here to note:
For example, if you call printName() with the command:
printName("Bob");
then, when printName() executes, the value of name is "Bob". If you call printName() by using a variable for an argument:
var user = "John"; printName(user);
then name has the value "John". If you were to add a line to printName() changing the value of name:
name = "Mr. " + name;
name would change, but the variable user, which was sent as an argument, would not change.
This leads to a discussion of variable scope. Variable scope refers to where a variable exists.
For instance, in the example printName(), name only exists within the function printName()it cannot be referred to or manipulated outside the function. It comes into existence when the function is called and ceases to exist when the function ends.
If the function is called again, a new instance of name is created.
In addition, any variable declared using the var command within the function will have a scope limited to the function.
If a variable is declared outside the body of a function, it is available throughout a scriptinside all functions and elsewhere.
If you declare a local variable inside a function that has the same name as an existing global variable, then inside the function, that variable name refers to the new local variable and not the global variable. If you change the value of
the variable inside the function, it does not affect the value of the global variable.
As mentioned in the previous section, functions can return results. Results are returned using the return statement.
The return statement can be used to return any valid expression that evaluates to a single value. For example, in the function cube(),
function cube(number) { var cube = number * number * number; return cube; }
the return statement will return the value of the variable cube. This function could just as easily have been written like this:
function cube(number) { return number * number * number; }
This works because the expression number * number * number evaluates to a single value.
As was mentioned in Chapter 3, "Working with Data and Information," there are compelling reasons to include function definitions inside the HEAD tags of the HTML file.
This ensures that all functions have been parsed before it is possible for user events to invoke a function.
This is especially relevant once you begin working with event handlers where incorrect placement of a function definition can mean an event can lead to a function call when the function has not been
evaluated and Navigator doesn't know it exists. When this happens, it causes an error message to be displayed.
To demonstrate the use of functions, you are going to rewrite the simple test question example you used in Listing 3.3.
In order to do this, you are going to create a function that receives a question as an argument, poses the question, checks the answer and returns an output string based on the accuracy of the user's response.
In order to do this, you need to learn the eval() method, which evaluates a string to a numeric value; for instance,
eval("10*10")
returns a numeric value of 100.
<HTML> <HEAD> <TITLE>Example 4.1</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //DEFINE FUNCTION testQuestion() function testQuestion(question) { //DEFINE LOCAL VARIABLES FOR THE FUNCTION var answer=eval(question); var output="What is " + question + "?"; var correct='<IMG SRC="correct.gif">'; var incorrect='<IMG SRC="incorrect.gif">'; //ASK THE QUESTION var response=prompt(output,"0"); //CHECK THE RESULT return (response == answer) ? correct : incorrect; } // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </HEAD< <BODY> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //ASK QUESTION AND OUTPUT RESULTS var result=testQuestion("10 + 10"); document.write(result); //STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </BODY> </HTML>
At first glance, this script may seem a little more complicated than the version used in Listing 3.3.
In reality, though, it simply separates the work into logical blocks and moves most of the work into the function testQuestion().
To understand the function, let's analyze the key lines.
function testQuestion(question) {
In this line, you define the function testQuestion() and indicate that it receives one argument, which is referred to as question within the function. In the case of this function, it is expected that question will be a string containing an arithmetic
expression.
var answer=eval(question);
The first thing you do after entering the function is to declare the variable answer and assign to it the numeric value of the arithmetic expression contained in the string question. This is achieved using the eval() function.
var output="What is " + question + "?"; var correct='<IMG SRC="correct.gif">'; var incorrect='<IMG SRC="incorrect.gif">';
In these lines you declare several more variables. The variable output contains the actual question to display, which is created using the concatenation operator.
var response=prompt(output,"0");
Here you ask the question and assign the user's response to the variable response.
return (response == answer) ? correct : incorrect;
In this line you use the conditional operator to check the user's response. The resulting value is returned by the return command.
Now that you understand the function, it should be clear how you are invoking it later in the body of the HTML file. The line
var result=testQuestion("10 + 10");
calls testQuestion() and passes a string to it containing an arithmetic expression. The function returns a result, which is stored in the variable result. Then you are able to output the result using document.write().
These two lines could be condensed into a single line:
document.write(testQuestion("10 + 10"));
End of Analysis
Now that you have seen an example of how functions work, lets' take a look at an application of functions called recursion.
For instance, the following is an example of a recursive function that calculates a factorial:
function factorial(number) { if (number > 1) { return number * factorial(number - 1); } else { return number; } }
At first glance, this function may seem strange. This function relies on the fact that the factorial of a number is equal to the number multiplied by the factorial of one less than the number. Expressed mathematically, this could be written:
x! = x * (x-1)!
In order to apply this formula, you have created a recursive function called factorial(). The function receives a number as an argument. Using the following if-else construct
if (number > 1) { return number * factorial(number - 1); } else { return number; }
the function either returns a value of 1 if the argument is equal to 1, or it applies the formula and returns the number multiplied by the factorial of one less than the number.
In order to do this, it must call the function factorial() from within the function factorial(). This is where the concept of variable scope becomes extremely important. It is important to realize that when the function calls factorial(), a new instance
of the function is being invoked, which means that a new instance of number is created. This continues to occur until the expression number-1 has a value of 1.
It is important to note that the function factorial() prevents infinite recursion because the if-else construct ensures that eventually the function will stop calling itself once the number passed to it is equal to one. In addition, if the function is
initially called with a value less than two, the function will immediately return without any recursion.
Using recursive functions, it is possible to extend the program used in Listing 4.1 so that it continues to ask the question until the user provides the correct answer, as shown in Listing 4.2.
<HTML> <HEAD> <TITLE>Example 4.2</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //DEFINE FUNCTION testQuestion() function testQuestion(question) { //DEFINE LOCAL VARIABLES FOR THE FUNCTION var answer=eval(question); var output="What is " + question + "?"; var correct='<IMG SRC="correct.gif">'; var incorrect='<IMG SRC="incorrect.gif">'; //ASK THE QUESTION var response=prompt(output,"0"); //CHECK THE RESULT return (response == answer) ? correct : testQuestion(question); } // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </HEAD< <BODY> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //ASK QUESTION AND OUTPUT RESULTS var result=testQuestion("10 + 10"); document.write(result); //STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </BODY> </HTML>
You will notice that you have only made a single change to the conditional expression
return (response == answer) ? correct : testQuestion(question);
Where you originally returned the value of the variable incorrect when the user provided an incorrect response, you are now returning the result of asking the question again (by calling testQuestion() again).
It is important to realize that this example could cause JavaScript to crash because of its memory handling problems if the user never provides the correct answer. This can be remedied by adding a counter to keep track of the number of chances the user
has to provide a correct answer:
<HTML> <HEAD> <TITLE>Example 4.2</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //DEFINE FUNCTION testQuestion() function testQuestion(question,chances) { //DEFINE LOCAL VARIABLES FOR THE FUNCTION var answer=eval(question); var output="What is " + question + "?"; var correct='<IMG SRC="correct.gif">'; var incorrect='<IMG SRC="incorrect.gif">'; //ASK THE QUESTION var response=prompt(output,"0"); //CHECK THE RESULT if (chances > 1) { return (response == answer) ? correct : testQuestion(question,chances-1); } else { return (response == answer) ? correct : incorrect; } } // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </HEAD< <BODY> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //ASK QUESTION AND OUTPUT RESULTS var result=testQuestion("10 + 10",3); document.write(result); //STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </BODY> </HTML>
By adding the if-else construct when you check the user's answer you are ensuring that you cannot enter an infinite recursion. The if-else construct could be replaced by a conditional expression:
return (response == answer) ? correct : ((chances > 1) ? testQuestion(question,chances-1) : incorrect);
What this expression says is if the user's response is correct (response==answer evaluates to true), then return the value of correct. Otherwise, if there are chances left (chances > 1 evaluates to true) ask the question again and return the result.
If there are no chances left and the answer is incorrect, return the value of the variable incorrect.
As you learned earlier, it is possible to use functions to build custom objects in JavaScript. In order to do this, you need to be able to define an object's properties, to create new instances
of objects, and add methods to objects.
Before creating a new object, it is necessary to define that object by outlining its properties. This is done by using a function that defines the name and properties
of the function. This type of function is known as a constructor function.
If you want to create an object type for students in a class, you could create an object named student with properties for name, age, and grade. This could be done with the function:
function student(name,age, grade) { this.name = name; this.age = age; this.grade = grade; }
Using this function, it is now possible to create an object using the new statement:
student1 = new student("Bob",10,75);
This line of JavaScript code creates an object called student1 with three properties: student1.name, student1.age, and student1.grade. This is known as an instance of the object student. By creating a new object student2 using the new statement, as
follows:
student2 = new student("Jane",9,82);
you would be creating a new instance of the object which is independent from student1.
It is also possible to add properties to objects once they are created simply by assigning values to a new property. For instance, if you want to add a property containing Bob's mother's name,
you could use the structure
student1.mother = "Susan";
This would add the property to student1 but would have no effect on student2 or future instances of the student object. To add the property mother to all instances of student, it would be necessary to add the property to the object definition before
creating instances of the object:
function student(name, age, grade, mother) { this.name = name; this.age = age; this.grade = grade; this.mother = mother; }
You can also use objects as properties of other objects. For instance, if you were to create an object called grade:
function grade (math, english, science) { this.math = math; this.english = english; this.science = science; }
You could then create two instances of the grade object for the two students:
bobGrade = new grade(75,80,77); janeGrade = new grade(82,88,75);
Using these objects, you could then create the student objects like this:
student1 = new student("Bob",10,bobGrade); student2 = new student("Jane",9,janeGrade);
You could then refer to Bob's math grade as student1.grade.math or Jane's science grade as student2.grade.science.
In addition to adding properties to object definitions, you can also add a method to an object definition. Because methods are essentially functions associated with an object, first you need to
create a function that defines the method you want to add to your object definition.
For instance, if you want to add a method to the student object to print the student's name, age, and grades to the document window, you could create a function called displayProfile():
function displayProfile() { document.write("Name: " + this.name + "<BR>"); document.write("Age: " + this.age + "<BR>"); document.write("Mother's Name: " + this.mother + "<BR>"); document.write("Math Grade: " + this.grade.math + "<BR>"); document.write("English Grade: " + this.grade.english + "<BR>"); document.write("Science Grade: " + this.grade.science + "<BR>"); }
Having defined the method, you now need to change the object definition to include the method:
function student(name,age, grade) { this.name = name; this.age = age; this.grade = grade; this.mother = mother; this.displayProfile = displayProfile; }
Then, you could output Bob's student profile by using the command
student1.displayProfile();
This would produce results similar to those in Figure 4.1.
Figure 4.1. The displayProfile() method displays the profile for any instance of the student object.
To further demonstrate the application of objects and defining your own objects, Listing 4.3 is a program that asks the user for personnel information of an employee and then formats it for
display on the screen.
In order to do this, you need to define an employee object, as well as a method for displaying the employee information.
<HTML> <HEAD> <TITLE>Example 6.3</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //DEFINE METHOD function displayInfo() { document.write("<H1>Employee Profile: " + this.name + "</H1><HR><PRE>"); document.writeln("Employee Number: " + this.number); document.writeln("Social Security Number: " + this.socsec); document.writeln("Annual Salary: " + this.salary); document.write("</PRE>"); } //DEFINE OBJECT function employee() { this.name=prompt("Enter Employee's Name","Name"); this.number=prompt("Enter Employee Number for " + this.name,"000-000"); this.socsec=prompt("Enter Social Security Number for " + this.name,"000-00-0000"); this.salary=prompt("Enter Annual Salary for " + this.name,"$00,000"); this.displayInfo=displayInfo; } newEmployee=new employee(); // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </HEAD> <BODY> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS newEmployee.displayInfo(); // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </BODY> </HTML>
Output
This script produces results similar to those in Figure 4.2 and 4.3.
Figure 4.2 The program prompts the user for the employee information.
Figures 4.2 and 4.3. The method you defined displays the formatted data.
In this example, the most noticeable variation on what you have learned is that you don't pass any arguments to the object definition function, employee().
Instead, this object definition is more of a dynamic object definition in that when a new instance of the object is created, the user is prompted for all the relevant data for the properties.
End of Analysis
Anyone who has programmed in other structured languages has probably encountered arrays of one sort or another and will be wondering where JavaScript's arrays are.
Arrays refer to ordered collections of values referred to by a single variable name. For instance, if you have an array named student, you might have the following ordered values:
student[0] = "Bob" student[1] = 10 student[2] = 75
Although JavaScript doesn't have a unique data type for arrays, properties and arrays are closely tied in JavaScript. In fact, arrays and properties are different ways of accessing the same information. In the example of the student object in the
preceding section, you can refer to the properties of the student2 object as:
student2["name"] student2["age"]
and so on, or by index numbers, where
student2[0] = "Jane" student2[1] = 9
and so on.
This capability to interchangeably treat object properties as an array will become particularly useful later when you learn about the for ... in loop structure in Chapter 7, "Loops."
To demonstrate how referring to object properties as an array can be useful, Listing 4.4 builds on the personnel information example in the Listing 4.3.
In this example, you do not have the user enter the personnel information for the new employee in the same way. You present a list of information you want. The user selects a number for the information she wants to enter. When they are done, they select
"0."
After the user finishes entering the information, then the script displays the formatted employee profile.
<HTML> <HEAD> <TITLE>Example 6.3</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS //DEFINE METHOD function displayInfo() { document.write("<H1>Employee Profile: " + this.name + "</H1><HR><PRE>"); document.writeln("Employee Number: " + this.number); document.writeln("Social Security Number: " + this.socsec); document.writeln("Annual Salary: " + this.salary); document.write("</PRE>"); } //DEFINE METHOD TO GET EMPLOYEE INFORMATION function getInfo() { var menu="0-Exit/1-Name/2-Emp. #/3-Soc. Sec. #/4-Salary"; var choice=prompt(menu,"0"); if (choice != null) { if ((choice < 0) || (choice > 4)) { alert ("Invalid choice"); this.getInfo(); } else { if (choice != "0") { this[choice-1]=prompt("Enter Information",""); this.getInfo(); } } } //DEFINE OBJECT function employee() { this.name=""; this.number=0; this.socsec=0; this.salary=0; this.displayInfo=displayInfo; this.getInfo=getInfo; } newEmployee=new employee(); // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </HEAD> <BODY> <SCRIPT LANGUAGE="JavaScript"> <!-- HIDE FROM OTHER BROWSERS newEmployee.getInfo(); newEmployee.displayInfo(); // STOP HIDING FROM OTHER BROWSERS --> </SCRIPT> </BODY> </HTML> var choice = prompt(menu,"0"); if (choice != "0") { this[choice] = prompt("Enter Information,",""); this.getInfo(); } } newEmployee.displayInfo;
Output
This script produces a series of results similar to those in Figures 4.4, 4.5, and 4.6.
Figure 4.4. A menu in a prompt box.
Figure 4.5. Prompting for input.
Analysis
In this example, you can see several of the concepts you have learned in action here, including recursion and associative arrays.
The method getInfo() needs some explanation:
var menu="0-Exit/1-Name/2-Emp. #/3-Soc. Sec. #/4-Salary";
The menu variable contains the string that presents the choices to the user. Notice the use of the \n special character to create a multi-line menu in a single text string.
var choice = prompt(menu,"0");
Here you present the menu to the user and ask for a choice which is stored in the variable choice.
if (choice != null) { if ((choice < 0) || (choice > 4)) { alert ("Invalid choice"); this.getInfo(); } else { if (choice != "0") { this[choice-1]=prompt("Enter Information",""); this.getInfo(); } } } }
This set of if statements is where the real work of the getInfo() method is done. The first if statement checks if the user has selected Cancel. If not, then the user's choice is checked to make sure it is in range (from zero to four). If the choice is
out of range, then the user is alerted, and the getInfo() method is called again. If the user's choice is in range, it is checked to see if the user has chosen zero for exit. If the user doesn't select 0, the user is prompted to enter the data he has
indicated. Then the getInfo() method is called again to present the menu again.
You will notice the use of the this keyword to refer to the current object and the use of this[choice-1] to refer to the array element (or property) selected by the user. Use choice-1 because the menu presents choices from 1 to 4 to the user, but array
indexes start from 0 and, in this case, it goes up to 3.
Functions provide a means to define segments of code which can be used more than once in a program. Like methods, which are part of objects, functions are defined by names, can be passed arguments and can return results.
Variable scope, whether a variable exists locally to a function or globally for the entire program, is an important concept in dealing with functions.
Recursive functions are functions that call themselves one or more times. Recursion is a powerful tool, but it needs to be used carefully to avoid infinite recursion, which occurs when a function repeatedly calls itself without ever ending. With the
current implementation of JavaScript, infinite recursion can't really happen because memory handling shortcomings mean that Navigator will crash when recursion gets too deep.
If you have made it to the end of this chapter you are making progress because both recursion and functions and objects are advanced topics.
Functions are also used to define the properties and objects which make up user-defined methods. Using the new keyword, it is possible to create multiple instances of an object which all exist independently.
Associative arrays are another way of referring to properties in an object.
In Chapter 5, you begin to work with events and event handlers which will allow you to design programs that interact with the user in a sophisticated way.
Command/Extension |
Type |
Description |
function |
JavaScript keyword |
Declares a function |
new |
JavaScript keyword |
Creates a new instance of an object |
eval() |
JavaScript method |
Evaluates a string to a numeric value |
|
|
|
JavaScript includes a method that performs the same thing as your power() function. The Math.pow() method is part of the Math object and is discussed in Chapter 10, "Strings, Math, and the History List."