User-Defined Functions

Motivation

We have worked with many pre-defined JavaScript/jQuery methods and functions. Some examples are:

A function is a named chunk of JavaScript code. A method is a function associated with some object. In the examples above, we used a method associated with a date object in a variable called today and two methods associated with a jQuery selection (an object that represents a set of page elements that match the given selection expression, "sel").

(Information you don't need to know: The functions alert() and prompt() are actually methods of the window object stored in the variable window — JavaScript will fill in window. automatically, but you can write window.prompt() if you want.)

When a function's name is used with parentheses (containing arguments, if any), the code associated with that name is executed. This is called invoking or calling the function.

Today we'll learn why we would want to define our own functions, how to define functions, and how to use functions.

The Farmer in the Dell, without functions

Suppose we wanted to put the lyrics of the old nursery rhyme The Farmer in the Dell into our web page. It would make sense just to type the lyrics into our HTML, but we're not going to do that. Instead, we're going to generate the text, just as we did with our madlibs. Bear with us, this is going someplace. Here is the first version of our code (v1):

This is div#farmer_v1

(FYI, the jQuery append method adds the contents to the selected page elements.)

Obviously, there's a lot of repetition in here. Not as bad as 99 bottles of beer on the wall but close. It's a song based on a pattern, and one fundamental purpose of functions is to capture patterns.

Note also the nonsense phrase Hi-ho, the derry-o in the middle. It turns out that varies from area to area. In London, they say Ee-i, tiddly-i. Another fundamental purpose of functions is to abstract code (by hiding details) and to modularize the code, avoiding repetition and allowing an update in a definition to affect any user of that definition. We saw this reasoning about abstraction and modularity in a slightly different form in CSS style sheets. It's the same idea.

The Farmer in the Dell, with refrain function

So, let's introduce a function into the code above to take care of that pesky bit of nonsense, which we'll name the refrain for lack of a better term. Here's the code (v2):

This is div#farmer_v2

The function keyword introduces a function definition. We'll get to the precise syntax below, but for now, just know that the code in braces is the definition of the function: it's the code that gets executed when the function is invoked. We invoke the function twice in the code below the function definition: that's the refrain() code.

Notice how defining and using this function solves the problem of switching from hi-ho the derry-o to Ee-i, tiddly-i or any other bit of nonsense and have it properly substituted in the correct places in the song. It doesn't seem to have saved us much typing, but you can imagine how much it might save if the definition were very long and complex.

For example, a function that prints the day (with name of the day of the week) time (in 12-hour format) and other information might well have a very long definition but would also be useful in a wide variety of contexts.

Note the distinction between defining a function and invoking one. Unless it is invoked, the function will not execute. Why would you ever define a function unless you were going to invoke it? Typically, you wouldn't, but you might be building a library of useful functions for someone else (even yourself) to use. We'll see how to build a library of JS functions below. jQuery is just such a library, and there are many others.

The ability to write something once and use it over and over is called modularity, a central principle in software engineering. Building programs out of reusable, mix-and-match units makes code easier to write, because you can write and debug a function once and then use it over and over again. The code is also easier to understand, because you can read and understand the function once and then know what's happening whenever it's used.

How to Define a Function

Let's look more precisely at the syntax of a function definition:

<script>
// comment about your lovely function
function functionName()
{ 
                            .
                            .
        JavaScript code to define the function goes here
                            .
                            .
} 

/* You would invoke your function in other
   JavaScript code, in this other <script> elements or in event
   handlers. */

</script>

To define your own function you need:

It is also considered good practice to put a comment before the function describing what the parameters are for, what value gets computed if any, what actions are performed, etc. Anything that isn't obvious from the code. (Comments that just repeat the code in English are bad.)

The Farmer in the Dell, with several functions

Now that we're a bit more comfortable with functions, let's use more of them. The refrain() function is invoked from several places, but it can be nice just to use functions to structure the code, naming chunks and pieces of it, so that the top-level function can be simpler and clearer. Here's an example (v3):

This is div#farmer_v3

Here is where we define a bunch of functions:

Notice how simple, even trivial, some of the function definitions are. This is not a bad thing! If something has gone wrong in the second verse, we know exactly where to look for the error, and we can easily ignore irrelevant code, such as refrain() or verse1().

Finally, we can write the song into the page by invoking the song function, as follows.

Sequence of Execution

So far in this course, you could count on things happening in a web page in a sensible order: top to bottom and left to right. If one tag comes before another in the text of the web page, it happens first. This sensible ordering didn't change when we got to JavaScript: JS also goes top to bottom and left to right. Then we got to conditionals, and things got a little odd, because we could now skip some lines of code. Still, we never went backwards or anything foolish like that. Alas, not any more.

As a synonym of the verb invoke, computer scientists use the verb call, as in Alice called Bob on her cell phone or this script element calls (invokes) the alert function. With that terminology in mind:

The order of execution with functions is that the caller executes first, top to bottom, but when the caller gets to the function invocation, control transfers to the "callee" (the function), which then executes top to bottom, and, when the function gets to the end, control returns to the caller, which continues from where it left off.

This all sounds very abstract, so let's go back to the multi-function version of Farmer in the Dell as a concrete example.

Observations:

As you can see, the body of a function follows the same top-to-bottom, left-to-right execution rules that we've always had. However, when the computer gets to a function invocation, it jumps to the beginning of the invoked function, executes that, and, when the function is done, returns to where it jumped from. Thus, it takes a round-trip journey to the function code. If the callee invokes another function, there is another round-trip within the outer round-trip. (These levels of round-trips can be embedded as deeply as we like.)

The thing to keep in mind, then, is that your function is not executed when the browser reads it, but only when some code invokes your function.

The Farmer in the Dell, with an abstract verse() function

There is another pattern in the nursery rhyme that you already noticed, which is that the unique phrase of each verse is repeated three times. In fact, you might have thought about defining the verse functions like this (v4) :

This is div#farmer_v4

Of course, we have to invoke the song function in order to write the song into the page:

The simplification of the two verse functions reveals a deeper pattern: the verse structure is identical, with the exception of the value of that phrase variable. Wouldn't it be nice to have an abstract verse() function that handled all the verses, instead of a separate function for each verse, as if they had nothing in common?

The following code (v5) does that:

Once again, we have to invoke the song function to start everything going:

This is div#farmer_v5

Note how clean and compact the definition of verse() is! Nevertheless, there's some new stuff here, namely parameters.

Function Parameters

A function can specify that it takes one or more inputs. In the function definition, these are a list of names enclosed in the parentheses after the function name. These names are called parameters.

Parameters are the names of variables that will be created every time the function is invoked. In the example above, the variable phrase is created and given a value each time the verse function is invoked. In order to specify what values these that parameter will hold when the function executes, the function invocation must be modified to include arguments — that is, comma-separated expressions within the parentheses after the function name. In the example above, the invocations are modified to include the text of the actual verse.

We have one more thing we will do with the Old MacDonald song, but first, let's take a digression to look at some other functions.

Point on a Line using Functions

Most of you first learned about functions in math class, and historically, the first computer languages used them the same way. We won't do much math in this course, but for the next few examples, we'll do a little very simple math. You're all familiar with the general equation of a line, that goes something like:

y = mx + b

or, using a notation with functions:

f(x) = mx + b

for particular line, we use particular values for the slope and y-intercept, such as:

f(x) = 3x + 2

In JavaScript (as we are using it in this class), we would might define a function to write the result using console.log.

The "\n" at the end of the result string stands for the "newline" character, which goes to the next line in many circumstances. We use it here for a bit of simplicity. Don't worry about it.

Execute this box (or change the code) and switch to the JavaScript console (using Firebug, for example), to see the output.


Note that this mathematical function brings out an important way to think about functions and parameters:

Thus, functions with parameters are hard to understand for the same reason that algebra is harder than arithmetic: most middle-school students struggle with abstract mathematical/algebraic expressions like a(b+c) even though they have no trouble with specific arithmetic expressions like 2(3+4).

Function Output to the Document

Let's return to our function that computes the y-value for points on a line and writes the result into the console.

Of course, switching to the JavaScript console is a little inconvenient. What would we do if we wanted to write the value into our document? We would, of course, create a destination element, such as the following div whose ID is f1_output1.

This is f2_output1.

And we would re-write our function (we'll give it a new name, f2) to append to that location, instead of using the console:

Try some values:


This works pretty well. But it's a little inflexible. What would we do if we wanted to compute the function but insert the result someplace else in our document? We'd have to re-write the function. That's not difficult in this case, but what if the math is very complicated, written by someone else, and loaded from a library?

For example, maybe the function is part of some tax calculation and it's written by the IRS (the Internal Revenue Service) and defined in some external JavaScript library. If the function always writes the output in an element with a particular ID, it would be frustratingly inflexible. Perhaps we don't expect better from the IRS, but we will try to do better.

Let's re-write our line function so that it can specify the output destination. That is, the function definition won't know where to write the result any more than it knows the value of x. Instead, it will be told where to write the result, using a parameter, just like it is told the specific value of x to use for a particular computation.

Here's the definition we will use. So that you can easily compare it with the inflexible definition, I've repeated the inflexible definition here, modified to write the output to a different element

Here are several places you could put the result:

This is f3_output1.
This is f3_output2.
This is f3_output3.

Let's try it out:


You can see that the f4 function is able to put the output anywhere, while the f3 function can only put the output into one place. In f3, the destination where the answer goes is hard-coded. (Computer Scientists use hard-coding to mean that something is inflexible: the caller has no choice in the matter.)

Of course, that flexibility comes at a (small) price: the caller now has the responsibility of specifying where the output is to go. If we omitted that second argument, treating f4 like f3, it would just fail: jQuery wouldn't know where to write the result, and so it wouldn't go into our document.

Farmer Song with flexible destination

Let's return to the Farmer in the Dell song, and allow it to be flexible about where the output goes. The following code (v6) is our ultimate version:

Now, when we want the song written into our page, we must specify what element we want it written into, just as we did with f4.

This is div#farmer_v6

Notice how easy it now is to change where the song is inserted onto the page! Also, notice that each function uses a different parameter name to stand for the place to put the output into:

It really doesn't matter what the function calls the parameter, any more than it matters whether f4 names its first parameter x. (The code would work just as well if we renamed all the x variables to x_value or horizontal_axis_position or even nonsense names like fred). As with variables, you should name your parameters something meaningful, but beyond that, you have great latitude in what you name them.

Finally, notice how compact our code is becoming. It's almost shorter than the song, and the structure of the code really reflects the way the song is organized. Moreover, we can generate another four-line verse by just adding one line of code to the definition of song.

Order of Arguments

When a function is called, the values of the argument expressions are stored in newly created variables with the parameter names, and the statements in the body of the function are executed. The argument values are matched up with the parameter names in order from left to right. For example, here is a function to compute someone's BMI and insert it onto the page. (BMI is calculated as a person's weight in kilograms divided by the square of their height in meters.)

This is div#bmi_v1

The following picture shows the correspondence of the particular arguments (150, 5, etc.) and the parameter names:

arguments match up to parameters in order

Obviously, it's important to get the order of the arguments correct: you don't want to accidentally compute the BMI for someone who is 7 foot 5 (instead of 5 foot 7) or even with a weight of 5 pounds and height of 7 foot 150!

Lifetime of Parameter Values

Another important thing to understand about parameters is that the storage location corresponding to a parameter only lasts as long as the function is executing. Furthermore, the parameter is a copy of the argument value, so the function can't modify a variable that it is given. Here's an example:

This is div#number_output

(Check the error console to figure out what happened to that last $("#number_output").append().

Scope of Parameter Names

Note that the x parameter of the next() function is only meaningful within that function. Any other x in any other part of the code means a different variable, if one even exists. For example, each of the following two functions uses the parameter h, yet these two parameters have nothing to do with each other.

This is div#output2

It happens that both of these functions have two parameters, the first a number and the second a string. Inside the function, there are several names, h, where and selector. The first parameter of each function is called h, but they have different values (2.5 versus 200) and different meaning. They happen to have the same name, but they are entirely different variables. The second parameters have the same value (the string "#output2"), but that's just another coincidence. They do have the same meaning, namely where the result will be appended, even though they have different names.

(You might object that it would be clearer to name these parameters something more specific than h, and you would be right, but the issue of happening to use a name that is used elsewhere never goes away.)

There are technical terms for these issues, and sometimes technical terms can bring out the important aspects of the examples. The issue of where a name has a single meaning is called scope, so we would say that the scope of the h parameter is within the function definition. That's why x was undefined outside the next() function.

The limitation on the scope of a variable name is a good thing, and it's related to this issue of modularity. The implementers of the hardness and is isTall functions don't have to coordinate about or even discuss what they name their parameters; the decision is entirely theirs.

The issue of how long a variable lasts is called extent. Parameters are very short-lived, they cease to exist when their function is finished executing. Variables that aren't inside a function definition are called global. Global variables last as long as the page is loaded in memory. [There are other kinds of extents, but this will do for now.] Global variables are called that because their scope is everyplace (within that file).

Note that the dieSlideNumber variable in the dice slide show example is a global variable.

Moving a Tennis Ball

One last example on the topic of global variables. The following code sets up a rectangular div whose background is a dark green, and on it is an image of a tennis ball. The dark green rectangle is the "court." The image of the tennis ball is positioned using absolute positioning, setting the top of the ball to be 0px down from the top of the court and the left edge of the ball is 100px from the left edge of the court.

tennis ball

Now for some JavaScript. We will define a global variable, x_position to keep track of the left position of the ball. Two functions will update that variable, making the number bigger or smaller. The updated position will then be used to change the position of the ball image, using the jQuery css method.

These two functions will then be attached as handlers for the click event handlers for the two buttons. (We'll learn about buttons when we get to forms.)

Try it! Click the buttons to make the ball go back and forth!

Exercise 0: Special-Purpose Prompts

Special-Purpose Prompts

Let's take some time to explore the functions that you are using in homework 5: promptDayHourMinute.js, or, with fancy syntax highlighting: promptDayHourMinute.js (highlighted)

Exercise 1: Better Tennis

Tennis Exercise

Using this tennis demo page, let's define some variations on the functions above that moved the tennis ball.

The functions left25() and right25()we defined above for the tennis example were fine but they miss an important pattern. Look at their code; it's nearly identical! Let's improve that.

How could we make the functions keep the ball in bounds?

Exercise 2: Adding to a Shopper's Total

This is a hard exercise. By all means think about it yourself, but we will probably talk about this in class.

Shopper Exercise

Let's now explore an example using user-defined functions. Write an HTML document to make a similar page. In your program, define a global variable called amountToPay which will store the user's total shopping bill. Be careful to initialize it properly. Define a function addToTotal() that updates and displays (via an alert()) amountToPay each time the user makes a purchase. addToTotal() should take a single parameter: the price of the purchased item. Your page should have a single form with multiple buttons (in our case, there are five).

For a simple version, each button will have its own click event handler that invokes addToTotal(), passing in the cost of the item purchased.

For a more sophisticated version, define just one event handler that uses a data structure (a JavaScript object) to look up the price by the ID and looks up the ID of this.

After you've worked on it on your own, you can view the source to see the solution.

Abstraction: Capturing Patterns with Functions

(This section is complex, but it's strongly related to the Farmer in the Dell series of examples. You should read it, but don't sweat over understanding it completely.)

JavaScript functions are a powerful way to abstract over patterns in JavaScript code as well as in HTML and CSS code.

As a practical example, suppose you want to create a page of links to CS110 student web pages. Such a page has a list of items that could be specified by HTML code like the following:

But such a list would be very tedious to create by hand for a class with lots of students. It would also be difficult to modify, e.g., suppose we want to add a hw2 link for each student. How much work would that be?

An alternative approach is based on the observation that all the list items are exactly the same except for (1) the last name, (2) the first name, (3) the server account name, (4) the year, (5) the lecture section, and (6) the discussion (lab) section. So we can create a function (let's call it writeStudent()) that takes these six elements as parameters (assumed to be string values) and uses jQuery's append method to construct one of the list items. We can then call the writeStudent() function for each student with the appropriate six string values for the parameters, as shown below:

function writeStudent(where, lastName, firstName, account, lecture) {
    $(where).append("<li>"
          + "<strong>" + lastName + ":</strong>"
          + lastName + ","
          + firstName + ", "
          + account + ", "
          + "lecture " + lecture + ", ");
    $(where).append("[<a href='http://cs.wellesley.edu/~" + account + "'>public</a>], ");
    $(where).append("[<a href='http://cs.wellesley.edu/~" + account + "/protected'>protected</a>], ");
    $(where).append("[<a href='http://cs.wellesley.edu/~" + account + "/hw1/hw1.html'>hw1</a>]");
}

writeStudent("#stu_list","Abbott", "Barbara", "babbott", "L03"); 
writeStudent("#stu_list","Babbage", "Ada", "ababbage", "L02"); 
... omitting many list items ...
writeStudent("#stu_list","Zuse", "Elizabeth", "ezuse", "L01"); 

Now to add an hw2 page for every student, we can simply insert add additional call to $(where).append(), copy/pasting and editing the code for doing hw1. This is way easier than adding an extra link for every student!

There is a small technical flaw in the code above. It will work, but the HTML isn't quite valid. Furthermore, writing complex HTML in a string like that is hard to get right; one missing quotation mark can mess it up entirely. In a later lecture, we'll learn an even better technique, using jQuery's cloning, which is much easier to debug than the complicated argument to the append method.

But we're not yet done with capturing patterns. Note that most of the code for the hyperlinks is almost exactly the same. This suggests that we should abstract over this pattern by writing a new function:

function appendURL(where, userName, file, text) {
    $(where).append(" [<a href='http://cs.wellesley.edu/~" 
                       + userName + "/" + file + "'>" + text + "</a>]");
}

function writeStudent(where, lastName, firstName, account, lecture) {
    $(where).append("<li>"
          + "<strong>" + lastName + ":</strong>"
          + lastName + ","
          + firstName + ", "
          + account + ", "
          + "lecture " + lecture + ", ");
      appendURL(where, account, "", "public");
      appendURL(where, account, "protected", "protected");
      appendURL(where, account, "hw1/hw1.html", "hw1");
      appendURL(where, account, "protected/hw2/hw2.html", "hw2");
}

// these function calls are unchanged!
writeStudent("#stu_list","Abbott", "Barbara", "babbott", "L03"); 
writeStudent("#stu_list","Babbage", "Ada", "ababbage", "L02"); 
... omitting many list items ...
writeStudent("#stu_list","Zuse", "Elizabeth", "ezuse", "L01"); 

Notice that even though we changed the code of the writeStudent function, we didn't have to change any of the invocations. That's because it did the same thing, with the same arguments, but did it in a different (better) way. This ability to improve the implementation of something without having to re-write all the callers is yet another aspect of modularity and good program design.

Putting Functions in Files

In our examples so far, we have defined JavaScript functions in script elements in our document. But just as it's possible to put CSS stylesheets in an external file that can be imported by many HTML documents, it's possible (and usually preferable) to define JavaScript functions in an external file that can be imported by many HTML documents.

In fact, earlier we mentioned the idea of creating a library of useful functions. Writing a bunch of function definitions into a separate JavaScript file and allowing others to load it is a simple yet sufficient step to create a library.

For instance, in our student list example, we could put the two functions in a file named students.js:

// Contents of the file students.js

function appendURL(where, userName, file, text) {
    $(where).append(" [<a href='http://cs.wellesley.edu/~" 
                       + userName + "/" + file + "'>" + text + "</a>]");
}

function writeStudent(where, lastName, firstName, account, lecture) {
    $(where).append("<li>"
          + "<strong>" + lastName + ":</strong>"
          + lastName + ","
          + firstName + ", "
          + account + ", "
          + "lecture " + lecture + ", ");
      appendURL(where, account, "", "public");
      appendURL(where, account, "protected", "protected");
      appendURL(where, account, "hw1/hw1.html", "hw1");
      appendURL(where, account, "protected/hw2/hw2.html", "hw2");
}

Then we can use the functions in students.js in any HTML file by using the src attribute of the script tag:

<!--import jQuery into this HTML file.-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<!--import the contents of students.js into this HTML file.-->
<script src="students.js"></script>

<ul id="class_roll">
</ul>

<script>
writeStudent("#class_roll","Abbott", "Barbara", "babbott", "2011", "L03", "D05"); 
writeStudent("#class_roll","Babbage", "Ada", "ababbage", "2013", "L02", "D01"); 
... omitting many list items ...
writeStudent("#class_roll","Zuse", "Elizabeth", "ezuse", "2010", "L01", "D04"); 
</script>

Summary

Things to note about functions:

Optional reading: Thau Chapter 6.

Solutions

  1. tennis demo solution