CS131 - Spring 2004

Lesson 6: User-defined Functions


Abstraction is the act of ignoring the minutiae and focusing on the big picture. We are constantly confronted with complex objects in life, objects much too complex to be understood by the average person. For example, to really understand how a television works, one must have extensive knowledge in electrical engineering, electronics, and physics. And yet, we all watch and operate television sets everyday. We do so by abstracting away unnecessary details and focusing on the features that are relevant to our needs. At the level of abstraction that most people view a television set, it is a box with several inputs (power cord, on/off button, channel selector, volume control) and two outputs (picture and sound). This level of understanding suffices to allow us to watch and have control over the television set.

You have already been introduced to the idea of abstraction in programming through JavaScript's predefined functions. Functions in a programming language represent units of computation whose details are abstracted away. For example, the computation involved in finding the square root of a number is certainly non-trivial, and yet the details of this process are abstracted away by the Math.sqrt function. Without worrying about how this function computes the square root, the programmer is able to call this function and use it effectively.

In this lesson, you will learn how to introduce your own abstractions into JavaScript by defining your own functions.


User-defined functions

Functions simplify the programmer's task in two major ways. First, functions help to minimize the amount of detail that the programmer must keep track of. Since the Math.sqrt function is available, the programmer does not need to remember the sequence of steps involved in computing the square root. She only needs to remember how to call the Math.sqrt function. Second, functions help to minimize the size and complexity of code. Once the Math.sqrt function has been defined, a single call to this function will suffice to compute a square root. Since the number of steps required to otherwise compute the square root would be considerable, this can represent a significant savings in the complexity of code.

JavaScript's predefined functions represent a collection of useful, general purpose abstractions. Since computations such as the square root and absolute value are common in many applications, the designers of JavaScript provided these abstractions for you in the form of predefined functions. In addition to these, you are able to define new abstractions by defining functions of your own design. Any computation that you find especially useful can be encapsulated as a function. Once defined and loaded into a Web page, your function can be called just like any predefined function, and so may be viewed as extending the capabilities of the JavaScript language.

As an example, recall the formula for converting a temperature in degrees Fahrenheit to degrees Celsius:

(5/9) * (tempInFahr - 32) In Lesson 4, you evaluated this expression for various values of tempInFahr. If converting temperatures was a task that you were to perform relatively often, you would spend a lot of time remembering and retyping this formula. Alternatively, you could encapsulate this computation in a function and save it. Any time you wanted to convert temperatures, you could then load this function and call it. You wouldn't need to remember the formula anymore, just the name of the function and how to call it.

The following is the definition of a JavaScript function that performs the temperature conversion. A description of the key parts of this definition are given below:

function fahrToCelsius(tempInFahr) // Assumes: tempInFahr is a temperature in Fahrenheit // Returns: the equivalent temperature in Celsius { return (5/9) * (tempInFahr - 32); }

In order to make a user-defined function accessible in a Web page, it must be included in that page. This can be accomplished in two ways. When the function is specific to that Web page only, its definition can be inserted directly into the HEAD of the HTML document, enclosed in SCRIPT tags. For example:

Alternatively, general-purpose function definitions can be entered into a separate text file and loaded as a function library as you did with prompt.js and random.js. You'll gain more experience with this later in the lesson.
EXERCISE 6.1:    In Lesson 4 you created a page named ftoc.html that converted Fahrenheit temperatures to Celsius temperatures. Modify your ftoc.html page to use the fahrToCelsius function defined above. That is, include the function in the HEAD of the document as shown above. Then modify your JavaScript code so that it uses this function to perform the temperature conversion. Include your modified ftoc.html page as your solution to this exercise.
EXERCISE 6.2:    In Lesson 4 you also created a page named ctof.html that converted Celsius temperatures to Fahrenheit. Modify this page to contain a function named celsiusToFahr in its HEAD. Be sure to include comments similar to those above in your new function. Also modify the page to use this function to perform the temperature conversion. Include your modified ctof.html page as your solution to this exercise.

Creating Functions Libraries

In exercises 1 and 2, the functions defined were specific to the pages in which they were defined, and would be unlikely to be used in a large number of pages. When you define functions that are likely to be reused, it makes sense to put them into a function library as was done with the functions in the prompt.js and random.js libraries. In practice a function library is simply a text file that contains JavaScript function definitions. Such a function library is then included into HTML documents using SCRIPT tags as you did in Lesson 5.

EXERCISE 6.3:    Create a text document named shapes.js. In this document, define a function named circleArea. This function should take as input the radius of a circle and return the area of a circle with that radius. Note: Function libraries do not contain any HTML tags such as <HTML> and <HEAD> so do not include them in this file. Include the contents of the shapes.js file as your solution to this exercise.

EXERCISE 6.4:    Create a new HTML page named ShapesTest.html that loads your shapes.js library. This page should prompt the user for the radius of a circle and then use your circleArea method to compute its area. Finally, the page should display the area of the circle. Include your ShapesTest.html page as your solution for this exercise.

Recall that to include a library of JavaScript functions, special SCRIPT tags that specify the file name for the library must be included in the HEAD of the page, e.g.,

As was the case with links and images, a file name that is not preceded with "http://" is assumed to be stored in the same directory as the referring Web page.

Functions can also accept multiple inputs. To define a function that accepts multiple inputs, you simply list each input, separated by commas, in the parenthesis following the function name. For example, the following function accepts 3 inputs:

EXERCISE 6.5:    Add the following functions to your shapes.js library:

To include more than one function in a library simply list them one after the other in the file. For an example see the contents of the random.js library file.

Once you have written these functions, modify your ShapesTest.html page to prompt the user for the length of the sides of a rectangle (use 2 prompts) and use your new functions to compute and display the area and circumference of the rectangle. Include the contents of your shapes.js library and your ShapesTest.html file as your solution to this exercise.


Functions with local variables

The functions for computing temperature conversions were simple enough that the entire computation could be performed in the return statement. For more complex computations, a single return statement may not suffice. For example, consider the following scheme for computing income tax.

This system can be described by the formula:

totalTax = 0.13 * Math.max(income - Math.max(itemized, 4150), 0);

This formula could be made much more clear by breaking it into steps, using temporary variables to store intermediate values:

deduction = Math.max(itemized, 4150); taxableIncome = Math.max(income - deduction, 0); totalTax = 0.13*taxableIncome

The following function encapsulates this computation:

function incomeTax(income, itemized) // Assumes: income >= 0, itemized >= 0 // Returns: flat tax (13%) due after deductions { var deduction, taxableIncome, totalTax; deduction = Math.max(itemized, 4150); taxableIncome = Math.max(income - deduction, 0); totalTax = 0.13*taxableIncome return totalTax; } The first line in this function is a variable declaration specifying that the new variables deduction, taxableIncome, and totalTax are to be used in the function. Declaring variables in this way signifies that they are local to the function, meaning they will only exist inside of the function. In this sense, local variables are similar to parameters. Declaring a local variable tells JavaScript that you are going to use it as temporary storage while computing the function value, and that the storage should go away when the function is done.

EXERCISE 6.6:    Create an HTML document named taxes.html and cut-and-paste the incomeTax function into the HEAD (enclosed in SCRIPT tags). In the BODY of the page, prompt the user for their income and itemized deduction, and display the amount of tax owed.

Use your page to determine the amount a person would owe with:

income = 100000.00 itemized = 12017.50 income = 42500.00 itemized = 8900.99 income = 13267.45 itemized = 14000.00
EXERCISE 6.7:    Most workers have part of their paychecks withheld every month to apply toward their income tax. When their taxes are filed at the end of the year, the total amount withheld during the year is subtracted from their computed tax to determine the amount that they owe. Modify the incomeTax function to have a third input, the amount withheld. The function should compute the amount owed by the taxpayer (stored in a variable named taxOwed) and return that value. Be sure to add your new variable to the variable declarations so that it will exist only inside the function. Similarly, modify your page so that it also prompts the user for the amount withheld and calls the function accordingly.

Use your page to determine the amount a person would owe with:

income = 100000.00 itemized = 12017.50 withheld = 10000.00 income = 42500.00 itemized = 8900.99 withheld = 6250.75 income = 13267.45 itemized = 14000.00 withheld = 0.00

In general, whenever new variables are to be used in a function, those variables should be declared, preceded by var, at the beginning of the function. Otherwise, the variables would persist after the function call was completed, and might even accidentally overwrite existing variables. Thus, the general form for function definitions is as follows:

function FUNCTION_NAME(PARAMETER_1, PARAMETER_2,..., PARAMETER_n) // Assumes: DESCRIPTION OF ASSUMPTIONS MADE ABOUT PARAMETERS // Returns: DESCRIPTION OF VALUE RETURNED BY FUNCTION { var LOCAL_1, LOCAL_2,..., LOCAL_m; STATEMENTS return EXPRESSION_SPECIFYING_FUNCTION_VALUE; }

where the function name, parameters, local variables, statements, and function value will differ depending on the task at hand. The return statement is optional, since some functions such as OldMacVerse are called in order to display values on the page as opposed to compute a return value.

EXERCISE 6.8:    Computer science often deals with extremes when it comes to time. Modern computers can perform millions of operations per second, and yet some tasks such as decoding an encrypted message still might require decades of computation. Create a page called years.html that will prompt the user for some number of years and display the corresponding time in seconds. To simplify this task, ignore leap years and assume that all years consist of exactly 365 days. For example, given an input value of 2.5, your page might display the following: You entered 2.5 years. That's 78840000 seconds!

Your page should include the definition of a function called yearsToSeconds that has one input, corresponding to a number of years, and returns the corresponding time in seconds. Feel free to use local variables to store temporary values in the computation. For example, you might first convert the number of years to days and store that value in a variable called numDays, then convert that value to hours, minutes, and finally seconds. Be sure to declare any variables that you use within the function.

Once you have your page working, use it to approximate how many seconds are left in this semester. In your college career? In your life? Report your results.


Function calling sequence

Whenever a function is called, a specific sequence of events takes place. If you understand this sequence, it should be possible to trace the execution of any function and ascertain its behavior. For example, consider the following page that computes the distance between two points.

<HTML> <!-- newdist.html 8/21/2000 --> <!----------------------------------------------------> <HEAD> <TITLE>Point Distance</TITLE> <SCRIPT LANGUAGE="JavaScript"> function distance(x1, y1, x2, y2) // Assumes: (x1,y1) and (x2,y2) are coordinates // Returns: the distance between the two points { var temp1, temp2; temp1 = Math.pow(x1 - x2, 2); temp2 = Math.pow(y1 - y2, 2); return Math.sqrt(temp1 + temp2); } </SCRIPT> </HEAD> <BODY> <SCRIPT LANGUAGE="JavaScript"> var x, y, d; x = 3; y = 4; d = distance(0, 0, x, y); document.write("<P>The distance is between (0,0) and " + "(" + x + ", " + y + ") is " + d); </SCRIPT> </BODY> </HTML>

When the distance function is called in the BODY of the page, the following events occur:

  1. The inputs to the function are evaluated first, with the variables x and y evaluating to 3 and 4, respectively.
  2. Next, execution shifts to the distance function to perform the actual computation.
  3. Within the function, new memory cells are allocated for the parameters x1, y1, x2, and y2. These memory cells are assigned the values of the corresponding inputs from the call: 0, 0, 3 and 4, respectively. Throughout the function, any references to the local variable names will be associated with these memory cells.
  4. Memory cells are next allocated (created) for each local variable: temp1, and temp2. Similarly, any references within the function to these names will be associated with these memory cells.
  5. The statements in the function are executed in order, assigning temp1 the value 9 and temp2 the value 16.
  6. When the return statement is reached, the expression in the statement is evaluated, yielding 5.
  7. After computing the return value, the execution of the function is over. Since the local variables exist only inside the function itself, the memory cells associated with the variables are deallocated, i.e., made available for other uses.
  8. Similarly, the memory cells associated with parameters are deallocated.
  9. Once the memory cells have been freed, 5 can be returned as the value of the function call. In this case, the value 5 replaces the function call in the assignment, and so d is assigned 5.
In general, the calling sequence for functions is as follows:
  1. The arguments in the function call are evaluated.
  2. Execution shifts to the function being called.
  3. Memory cells are allocated (created) for each parameter in the function, and the values of the corresponding inputs are assigned to these memory cells.
  4. Memory cells are allocated (created) for each local variable.
  5. The statements in the body of the function are executed in order.
  6. When a return statement is encountered, the expression is evaluated.
  7. Memory cells associated with the local variables are deallocated (destroyed).
  8. Memory cells associated with the parameters are deallocated (destroyed).
  9. Upon return, the value of the expression in the return statement replaces the function call in whatever expression it appears in.
EXERCISE 6.9:    As in the example above, trace the sequence of events that occur as a result of the call to distance in the following write statement: document.write( distance(2, 1+1, 3+2, 6) );

Cut-and-paste the newdist.html text into a new HTML document and modify the page to verify your final answer.

EXERCISE 6.10:    Similarly, trace the sequence of events that occur as a result of the call to the incomeTax function in the following assignment: x = incomeTax(25000 + 10000, 4500, 1100);

Verify your final answer using your taxes.html page.

JavaScript variables that appear in the BODY of a Web page are known as global variables since they can be accessed from anywhere, including functions defined in the HEAD. Through parameters and local variables, a function has the ability to create variables that exist and are visible only inside that function. For example, within the distance function, the parameters x1, y1, x2, and y2 are accessible, as are the local variables temp1, and temp2. The global variables x and y are also accessible within the function, but are not actually used here.

Since each function in a page can have its own local variables, it is possible to have the same variable name refer to distinct memory cells. For example, suppose the local variables temp1 and temp2 in the function were instead named x and y (identical to the global variables in the BODY). While this might be confusing to the reader, the browser is not confused at all. The global variables named x and y are separate from the local variables x and y, with the local variables being accessible only from within the distance function. Assignments made to x and y within the function would affect the local variables only, leaving the global variables untouched.

EXERCISE 6.11:    Verify that global and local variables behave as described above by modifying the newdist.html page. Rename the local variables in the function x and y and verify that the output of the page is unchanged.

Once again, the idea behind functions is to build up independent units of abstraction. We would like to be able to call a function and not worry about how it works. Declaring variables inside a function to be local is instrumental in achieving this level of abstraction. If all variables used inside a function are declared to be local, then the execution of that function is not affected by nor does it affect any external variables. If functions access and change global variables, however, the results may be difficult to predict or control. For example, suppose that the variables in the distance function were named x and y but not declared to be local (using the var declaration). If that were the case, then assignments to those variables would affect the global variables that appear in the BODY.

EXERCISE 6.12:    To demonstrate this undesirable effect, modify your newdist.html page further by commenting out the variable declarations in the distance function. That is, place double slashes '//' at the front of that line of code. Assuming you still have the variables in the function named x and y, what output is produced by this modified page? Explain why the output is different from the output in the last exercise?

Lesson Summary


Solutions to odd numbered exercises.