Chapter 4
In This Chapter
Passing parameters into functions
Returning values from functions
Functions and variable scope
Producing basic arrays
Retrieving data from arrays
Building a multidimensional array
Creating objects
Building object constructors
Introducing JSON notation
It doesn't take long for your code to become complex. Soon enough, you find yourself wanting to write more sophisticated programs. When things get larger, you need new kinds of organizational structures to handle the added complexity.
You can bundle several lines of code into one container and give this new chunk of code a name: a function. You can also take a whole bunch of variables, put them into a container, and give it a name. That's called an array. If you combine functions and data, you get another interesting structure called an object.
You may have encountered variables and functions in their simplest forms elsewhere in this book (variables were first introduced in Chapter 1 of this minibook, and functions made their appearance in Chapter 2). This chapter is about how to work with more code and more data without going crazy.
Functions come in handy when you're making complex code easier to handle — a useful tool for controlling complexity. You can take a large, complicated program and break it into several smaller pieces. Each piece stands alone and solves a specific part of the overall problem.
You can think of each function as a miniature program. You can define variables in functions, put loops and branches in there, and do anything else you can do with a program. A program using functions is basically a program full of subprograms.
To explain functions better, think back to an old campfire song, “The Ants Go Marching.” Figure 4-1 re-creates this classic song for you in JavaScript format. (You may want to roast a marshmallow while you view this program.)
If you're unfamiliar with this song, it simply recounts the story of a bunch of ants. The littlest one apparently has some sort of attention issues. During each verse, the little one gets distracted by something that rhymes with the verse number. The song typically has ten verses, but I'm just doing two for the demo.
Before you look at the code, think about the structure of the song, “The Ants Go Marching.” Like many songs, it has two parts. The chorus is a phrase repeated many times throughout the song. The song has several verses, which are similar to each other, but not quite identical.
Think about the song sheet passed around the campfire. (I'm getting hungry for a s'more.) The chorus is usually listed only one time, and each verse is listed. Sometimes, you have a section somewhere on the song sheet that looks like the following:
Verse 1
Chorus
Verse 2
Chorus
Musicians call this a road map, and that's a great name for it. A road map is a high-level view of how you progress through the song. In the road map, you don't worry about the details of the particular verse or chorus. The road map shows the big picture, and you can look at each verse or chorus for the details.
Take a look at the code for antsFunction.html and see how it reminds you of the song sheet for “The Ants Go Marching”:
<!DOCTYPE html>
<html lang = "en-US">
<head>
<meta charset = "UTF-8">
<title>antsFunction.html</title>
<script type = "text/javascript">
//from antsFunction.html
var output;
function chorus() {
var text = "...and they all go marching down <br />";
text += "to the ground <br />";
text += "to get out <br />";
text += "of the rain. <br />";
text += " <br />";
text += "boom boom boom boom boom boom boom boom <br /><br />";
output.innerHTML += text;
} // end chorus
function verse1(){
var text = "The ants go marching 1 by 1 hurrah, hurrah <br />";
text += "The ants go marching 1 by 1 hurrah, hurrah <br />";
text += "The ants go marching 1 by 1 <br />";
text += " The little one stops to suck his thumb <br /><br />";
output.innerHTML += text;
} // end verse1
function verse2(){
var text = "The ants go marching 2 by 2 hurrah, hurrah <br />";
text += "The ants go marching 2 by 2 hurrah, hurrah <br />";
text += "The ants go marching 2 by 2 <br />";
text += " The little one stops to tie his shoe <br /><br />";
output.innerHTML += text;
} // end verse2
function makeSong(){
output = document.getElementById("output");
output.innerHTML = "";
verse1();
chorus();
verse2();
chorus();
} // end makeSong
</script>
</head>
<body>
<h1>Using Basic Functions</h1>
<form action = "">
<fieldset>
<button type = "button"
onclick = "makeSong()">
make song
</button>
</fieldset>
</form>
<div id = "output">
The song will appear here...
</div>
</body>
</html>
The program code breaks the parts of the song into the same pieces a song sheet does. Here are some interesting features of antsFunction.html:
Functions are logically separated from each other. This separation is a good thing because it prevents certain kinds of errors. However, sometimes you want to send information to a function. You may also want a function to return some type of value. The antsParam.html page rewrites the “The Ants Go Marching” song in a way that takes advantage of function input and output.
<!DOCTYPE HTML>
<html lang = "en">
<head>
<title>param.html</title>
<meta charset = "UTF-8" />
<style type = "text/css">
</style>
<script type = "text/javascript">
//Ants to marching in using functions with parameters
function makeSong(){
//create output variable
var output = document.getElementById("output");
output.innerHTML = "";
output.innerHTML += verse(1);
output.innerHTML += chorus();
output.innerHTML += verse(2);
output.innerHTML += chorus();
} // end makeSong
function chorus(){
var result = "-and they all go marching down, <br />";
result += "to the ground, to get out, of the rain. <br />";
result += "boom boom boom boom <br />";
result += "boom boom boom boom <br />";
result += "<br />";
return result;
} // end chorus
function verse(verseNumber){
var distraction = "";
if (verseNumber == 1){
distraction = "suck his thumb";
} else if (verseNumber == 2){
distraction = "tie his shoe";
} else {
distraction = "there's a problem here...";
} // end if
var result = "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + "<br />";
result += "The little one stops to ";
result += distraction + "<br /><br /> ";
return result;
} // end verse
</script>
This code incorporates a couple of important new ideas. (The following list is just the overview; the specifics are coming in the following sections.)
The makeSong code has been changed in one significant way. In the last program, the makeSong code called the functions, which did all the work. This time, the functions don't actually output anything themselves. Instead, they collect information and pass it back to the main program. Inside the makeSong code, each function is treated like a variable.
You've seen this behavior before. The prompt() method returns a value. Now the chorus() and verse() methods return values. You can do anything you want to this value, including storing it to a variable, printing it, or comparing it to some other value.
The chorus of “The Ants Go Marching” song program has been changed to return a value. Take another look at the chorus() function to see what I mean.
function chorus(){
var result = "-and they all came marching down, <br />";
result += "to the ground, to get out, of the rain. <br />";
result += "boom boom boom boom <br />";
result += "boom boom boom boom <br />";
result += "<br />";
return result;
} // end chorus
Here's what changed:
The verse() function is quite interesting:
To make the verse so versatile (get it? verse-atile!), it must take input from the primary program and return output.
The verse() function is always called with a value inside the parentheses. For example, the main program sets verse(1) to call the first verse, and verse(2) to invoke the second. The value inside the parentheses is called an argument.
The verse function must be designed to accept an argument (because I call it using values inside the parentheses). Look at the first line to see how.
function verse(verseNumber){
In the function definition, I include a variable name. Inside the function, this variable is known as a parameter. (Don't get hung up on the terminology. People often use the terms parameter and argument interchangeably.) The important idea is that whenever the verse() function is called, it automatically has a variable called verseNumber. Whatever argument you send to the verse() function from the main program will become the value of the variable verseNumber inside the function.
You can define a function with as many parameters as you want. Each parameter gives you the opportunity to send a piece of information to the function.
If you know the verse number, you can determine what distracts “the little one” in the song. You can determine the distraction in a couple ways, but a simple if-elseif structure is sufficient for this example.
var distraction = "";
if (verseNumber == 1){
distraction = "suck his thumb.";
} else if (verseNumber == 2){
distraction = "tie his shoe.";
} else {
distraction = "I have no idea.";
}
I initialized the variable distraction to be empty. If verseNum is 1, set distraction to "suck his thumb". If verseNumber is 2, distraction should be "tie his shoe". Any other value for verseNumber is treated as an error by the else clause.
By the time this code segment is complete, verseNumber and distraction both contain a legitimate value.
When you know these variables, it's pretty easy to construct the output text:
var result = "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + "<br />";
result += "The little one stops to ";
result += distraction + "<br /><br /> ";
return result;
} // end verse
A whole lotta concatenating is going on, but it's essentially the same code as the original verse() function. This one's just a lot more flexible because it can handle any verse. (Well, if the function has been preloaded to understand how to handle the verseNumber.)
A function is much like an independent mini-program. Any variable you create inside a function has meaning only inside that function. When the function is finished executing, its variables disappear! This setup is actually a really good thing. A major program will have hundreds of variables, and they can be difficult to keep track of. You can reuse a variable name without knowing it or have a value changed inadvertently. When you break your code into functions, each function has its own independent set of variables. You don't have to worry about whether the variables will cause problems elsewhere.
You can also define variables at the main (script) level. These variables are global variables. A global variable is available at the main level and inside each function. A local variable (one defined inside a function) has meaning only inside the function. The concept of local versus global functions is sometimes referred to as scope.
Local variables are kind of like local police. Local police have a limited geographical jurisdiction, but they're very useful within that space. They know the neighborhood. Sometimes, you encounter situations that cross local jurisdictions. This situation is the kind that requires a state trooper or the FBI. Local variables are local cops, and global variables are the FBI.
To understand the implications of variable scope, take a look at scope.html:
<script type = "text/javascript">
//from scope.html
var globalVar = "I'm global!";
function myFunction(){
var localVar = "I'm local";
console.log(localVar);
}
myFunction();
</script>
This program defines two variables. In the main code, globalVar is defined, and localVar is defined inside a function. If you run the program in debug mode while watching the variables, you can see how they behave. Figure 4-2 shows what the program looks like early in the run.
localVar doesn't have meaning until the function is called, so it remains undefined until the computer gets to that part of the code. Step ahead a few lines, and you see that localVar has a value, as shown in Figure 4-3.
globalVar still has a value (it's an FBI agent), and so does localVar because it's inside the function.
If you move a few more steps, localVar no longer has a value when the function ends (see Figure 4-4).
Variable scope is a good thing because it means you have to keep track of only global variables and the variables defined inside your current function. The other advantage of scope is the ability to reuse a variable name. You can have ten different functions all using the same variable name, and they won't interfere with each other because they're entirely different variables.
If functions are groups of code lines with a name, arrays are groups of variables with a name. Arrays are similar to functions because they're used to manage complexity. An array is a special kind of variable. Use an array whenever you want to work with a list of similar data types.
The following code shows a basic demonstration of arrays:
<script type = "text/javascript">
//from genres.html
//creating an empty array
var genre = new Array(5);
//storing data in the array
genre[0] = "flight simulation";
genre[1] = "first-person shooters";
genre[2] = "driving";
genre[3] = "action";
genre[4] = "strategy";
//returning data from the array
alert ("I like " + genre[4] + " games.");
//]]
</script>
The variable genre is a special variable because it contains many values. Essentially, it's a list of genres. The new Array(5) construct creates space in memory for five variables, all named genre.
After you specify an array, you can work with the individual elements using square-bracket syntax. An integer identifies each element of the array. The index usually begins with.
genre[0] = "flight simulation";
The preceding code assigns the text value “flight simulation” to the genre array variable at position 0.
After you store the data in the array, you can use the same square-bracket syntax to read the information.
The line
alert ("I like " + genre[4] + " games.");
finds element 4 of the genre array and includes it in an output message.
Figure 4-5 shows a run of genres.html.
The main reason to use arrays is convenience. When you have a lot of information in an array, you can write code to work with the data quickly. Whenever you have an array of data, you commonly want to do something with each element in the array. Take a look at games.html to see how you can do so:
<script type = "text/javascript">
//from games.html
//pre-loading an array
var gameList = new Array("Flight Gear", "Sauerbraten", "Future Pinball",
"Racer", "TORCS", "Orbiter", "Step Mania", "NetHack",
"Marathon", "Crimson Fields");
var text = "";
for (i = 0; i < gameList.length; i++){
text += "I love " + gameList[i] + "
";
} // end for loop
alert(text);
</script>
Notice several things in this code:
If you read the earlier sections, you probably just got that marching ant song out of your head. Sorry. Take a look at the following variation, which uses arrays and loops to simplify the code even more.
<script type = "text/javascript">
//This old man using functions and arrays
var distractionList = Array("", "suck his thumb", "tie his shoe",
"climb a tree", "shut the door");
function makeSong(){
//create output variable
var output = document.getElementById("output");
output.innerHTML = "";
for (verseNumber = 1; verseNumber < distractionList.length; verseNumber++){
output.innerHTML += verse(verseNumber);
output.innerHTML += chorus();
} // end for loop
} // end makeSong
function chorus(){
var result = "-and they all came marching down, <br />";
result += "to the ground, to get out, of the rain. <br />";
result += "boom boom boom boom <br />";
result += "boom boom boom boom <br />";
result += "<br />";
return result;
} // end chorus
function verse(verseNumber){
var distraction = distractionList[verseNumber];
var result = "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + ", ";
result += "hurrah, hurrah <br />";
result += "The ants go marching ";
result += verseNumber + " by " + verseNumber + "<br />";
result += "The little one stops to ";
result += distraction + "<br /><br /> ";
return result;
} // end verse
</script>
This code is just a little different from the antsParam program shown in the section of this chapter called “Passing Data to and from Functions.”
Arrays are useful when working with lists of data. Sometimes, you encounter data that's best imagined in a table. For example, what if you want to build a distance calculator that determines the distance between two cities? The original data might look like Table 4-1.
Think about how you would use Table 4-1 to figure out a distance. If you wanted to travel from New York to London, for example, you'd pick the New York row and the London column and figure out where they intersect. The data in that cell is the distance (3,470 miles).
When you look up information in any kind of a table, you're actually working with a two-dimensional data structure — a fancy term, but it just means table. If you want to look something up in a table, you need two indices, one to determine the row and another to determine the column.
If this concept is difficult to grasp, think of the old game Battleship. The playing field is a grid of squares. You announce I-5, meaning column I, row 5, and the opponent looks in that grid to discover that you've sunk his battleship. In programming, you typically use integers for both indices, but otherwise, it's exactly the same as Battleship. Any time you have two-dimensional data, you access it with two indices.
Often, we call the indices row and column to help you think of the structure as a table. Sometimes, other names more clearly describe how the behavior works. Take a look at Figure 4-7, and you see that the distance.html program asks for two cities and returns a distance according to the data table.
This program is a touch longer than some of the others, so I break it into parts in the following sections for easy digestion. Be sure to look at the program in its entirety on the website.
The key to this program is the data organization. The first step is to set up two arrays.
<script type = "text/javascript">
//from distance.html
//cityName has the names of the cities
cityName = new Array("Indianapolis", "New York", "Tokyo", "London");
//create a 2-dimension array of distances
distance = new Array (
new Array (0, 648, 6476, 4000),
new Array (648, 0, 6760, 3470),
new Array (6476, 6760, 0, 5956),
new Array (4000, 3470, 5956, 0)
);
The first array is an ordinary single-dimension array of city names. I've been careful to always keep the cities in the same order, so whenever I refer to city 0, I'm talking about Indianapolis (my hometown), New York is always going to be at position 1, and so on.
The cityNames array has two jobs. First, it reminds me what order all the cities will be in, and, second, it gives me an easy way to get a city name when I know an index. For example, I know that cityName[2] will always be “Tokyo”.
The distance array is very interesting. If you squint at it a little bit, it looks a lot like Table 4-1, shown earlier in this chapter. That's because it is Table 4-1, just in a slightly different format.
distance is an array. JavaScript arrays can hold just about everything, including other arrays! That's what distance does. It holds an array of rows. Each element of the distance array is another (unnamed) array holding all the data for that row. If you want to extract information from the array, you need two pieces of information. First, you need the row. Then because the row is an array, you need the column number within that array. So, distance[1][3] means go to row 1 (“New York”) of distance. Within that row go to element 3 (“London”) and return the resulting value (3470). Cool, huh?
The program requires that you ask for two cities. You want the user to enter a city number, not a name, and you want to ask this question twice. Sounds like a good time for a function.
function getCity(){
// presents a list of cities and gets a number corresponding to
// the city name
var theCity = ""; //will hold the city number
var cityMenu = "Please choose a city by typing a number:
";
cityMenu += "0) Indianapolis
";
cityMenu += "1) New York
";
cityMenu += "2) Tokyo
";
cityMenu += "3) London
";
theCity = prompt(cityMenu);
return theCity;
} // end getCity
The getCity() function prints a little menu of city choices and asks for some input. It then returns that input.
The main() function handles most of the code for the program.
function main(){
var output = "";
var from = getCity();
var to = getCity();
var result = distance[from][to];
output = "The distance from " + cityName[from];
output += " to " + cityName[to];
output += " is " + result + " miles.";
alert(output);
} // end main
main();
The main() function controls traffic. Here's what you do:
The point of this function is to create some text output describing the distance. I begin by creating a variable called output and setting its initial value to empty.
Fortunately, you have a great function called getCity() that handles all the details of getting a city in the right format. Call this function and assign its value to the new variable from.
That getCity() function sure is handy. Use it again to get the city number you'll call to.
Because you know two indices, and you know they're in the right format, you can simply look them up in the table. Look up distance[from][to] and store it in the variable result.
Use concatenation to build a suitable response string and send it to the user.
The program uses numeric indices for the cities, but they don't mean anything to the user. Use the cityNames array to retrieve the two city names for the output.
Only one line of code doesn't appear in a function. That line calls the main() function and starts the whole thing.
So far you've used a lot of wonderful objects in JavaScript, like the document object and the array object. However, that's just the beginning. It turns out you can build your own objects too, and these objects can be very powerful and flexible. Objects typically have two important components: properties and methods. A property is like a variable associated with an object. The properties taken together describe the object. A method is like a function associated with an object. The methods describe things the object can do. If functions allow you to put code segments together and arrays allow you to put variables together, objects allow you to put both code segments and variables (and functions and arrays) in the same large construct.
JavaScript makes it trivially easy to build an object. Because a variable can contain any value, you can simply start treating a variable like an object and it becomes one.
Figure 4-8 shows a critter that has a property.
Take a look at the following code:
//from basicObject.html
//create the critter
var critter = new Object();
//add some properties
critter.name = "Milo";
critter.age = 5;
//view property values
alert("the critter's name is " + critter.name);
The way it works is not difficult to follow:
JavaScript has a built-in object called Object. Make a variable with the new Object() syntax, and you'll build yourself a shiny, new standard object.
A property is a subvariable. It's nothing more than a variable attached to a specific object. When you assign a value to critter.name, for example, you're specifying that critter has a property called name and you're also giving it a starting value.
Just keep adding properties. This allows you to group a number of variables into one larger object.
Unlike arrays where it's common for all the elements to contain exactly the same type of data, each property can have a different type.
If the critter object has a name property, you can use critter.name as a variable. Like other variables, you can change the value by assigning a new value to critter.name or you can read the content of the property.
Objects have other characteristics besides properties. They can also have methods. A method is simply a function attached to an object. To see what I'm talking about, take a look at this example:
//create the critter
//from addingMethods.html
var critter = new Object();
//add some properties
critter.name = "Milo";
critter.age = 5;
//create a method
critter.talk = function(){
msg = "Hi! My name is " + this.name;
msg += " and I'm " + this.age;
alert(msg);
} // end method
// call the talk method
critter.talk();
This example extends the critter object described in the last section. In addition to properties, the new critter has a talk() method. If a property describes a characteristic of an object, a method describes something the object can do. Figure 4-9 illustrates the critter showing off its talk() method:
Here's how it works:
Begin by building an object and giving it some properties.
In fact, methods are properties in JavaScript, but don't worry too much about that; it'll make your head explode.
If you created a function that you want to use as a method, you can simply assign it.
More often, you'll want to create your method right there as you define the object. You can create a function immediately with the function(){ syntax.
Inside the function, you may want to access the properties of the object. this.name refers to the name property of the current object.
After you define an object with a method, you can invoke it. For example, if the critter object has a talk method, use critter.talk() to invoke this method.
These objects are nice, but what if you want to build several objects with the same definition? JavaScript supports an idea called a constructor, which allows you to define an object pattern and reuse it.
Here's an example:
//building a constructor
//from constructor.html
function Critter(lName, lAge){
this.name = lName;
this.age = lAge;
this.talk = function(){
msg = "Hi! My name is " + this.name;
msg += " and I'm " + this.age;
alert(msg);
} // end talk method
} // end Critter class def
function main(){
//build two critters
critterA = new Critter("Alpha", 1);
critterB = new Critter("Beta", 2);
critterB.name = "Charlie";
critterB.age = 3;
//have 'em talk
critterA.talk();
critterB.talk();
} // end main
main();
This example involves creating a class (a pattern for generating objects) and reusing that definition to build two different critters. First, look over how the class definition works:
After you define a class, you can reuse it. Look again at the main function to see how I use my newly minted Critter class:
function main(){
//build two critters
critterA = new Critter("Alpha", 1);
critterB = new Critter("Beta", 2);
critterB.name = "Charlie";
critterB.age = 3;
//have 'em talk
critterA.talk();
critterB.talk();
} // end main
main();
After you define a class, you can use it as a new data type. This is a very powerful capability. Here's how it works:
JavaScript objects and arrays are incredibly flexible. In fact, they are so well known for their power and ease of use that a special data format called JavaScript Object Notation (JSON) has been adopted by many other languages.
JSON is mainly used as a way to store complex data (especially multidimensional arrays) and pass the data from program to program. JSON is essentially another way of describing complex data in a JavaScript object format. When you describe data in JSON, you generally do not need a constructor because the data is used to determine the structure of the class.
JSON data is becoming a very important part of web programming because it allows an easy mechanism for transporting data between programs and programming languages.
To see how JSON works, look at this simple code fragment:
var critter = {
"name": "George",
"age": 10
};
This code describes a critter. The critter has two properties, a name and an age. The critter looks much like an array, but rather than using a numeric index like most arrays, the critter has string values to serve as indices. It is in fact an object.
You can refer to the individual elements with a variation of array syntax, like this:
alert(critter["name"]);
You can also use what's called dot notation (as used in objects) like this:
alert(critter.age);
Both notations work the same way. Most of the built-in JavaScript objects use dot notation, but either is acceptable.
To store data in JSON notation:
You can use the var statement like you do any variable.
This is the same mechanism you use to create a preloaded array (as described earlier in this chapter).
For the critter, I want the properties to be named “name” and “age” rather than numeric indices. For each property, I begin with the property name. The key can be a string or an integer.
You can then associate any type of value you want with the key. In this case, I associate the value George with the key name.
You can add as many name/value pairs as you wish.
JSON is convenient because it can be used to handle quite complex data structures. For example, look at the following (oddly familiar) data structure written in JSON format:
var distance = {
"Indianapolis" :
{ "Indianapolis": 0,
"New York": 648,
"Tokyo": 6476,
"London": 4000 },
"New York" :
{ "Indianapolis": 648,
"New York": 0,
"Tokyo": 6760,
"London": 3470 },
"Tokyo" :
{ "Indianapolis": 6476,
"New York": 6760,
"Tokyo": 0,
"London": 5956 },
"London" :
{ "Indianapolis": 4000,
"New York": 3470,
"Tokyo": 5956,
"London": 0 },
};
This data structure is another way of representing the distance data used to describe two-dimension arrays. This is another two-dimension array, but it is a little different than the one previously described.
Setting up the data in this way seems a bit tedious, but it's very easy to work with. The city names are used directly to extract data, so you can find the distance between two cities with array-like syntax:
alert(distance["Indianapolis"]["London"]);
If you prefer, you can use the dot syntax:
alert(distance.Indianapolis.Tokyo);
You can even go with some kind of hybrid:
alert(distance["London"].Tokyo);
JSON has a number of important advantages as a data format: