What you will learn
Functions are an essential part of program design. You can use functions to break up a large solution into individual components and to create libraries of behaviors that can be used by your programs. We have used some built-in functions (for example, alert
) and created event handler functions (for example, doCheckAge
). In this chapter, you’ll learn how to create and use functions of your own. You’ll see how to give functions data to work on and how a program can receive results that a function returns. Along the way, you will pick up some tips about error handling.
A function is a chunk of JavaScript that has been given an identifier. A function should be defined before it is used. When JavaScript encounters a function definition, it takes the statements that provide the behavior of the function and stores them, so they are ready for use by the program. A program can call the function and the statements in the function will be performed. We’ve created and called functions already, but now is the time to take a detailed look at how they work. Let’s start with a look at a simple function that just says “hello.”
This web page contains a function called greeter
. The function finds an element on the screen with the ID outputParagraph
and displays Hello
in that element. Once the function has been defined, a program can call it. A call of a function runs the statements given when the function was defined. The greeter
function doesn’t do much, but you can create functions that contain many statements. Remember that your program must define the function before it can be called.
CODE ANALYSIS
Investigating functions
Start by opening the application in the Ch07 FunctionsCh07-01 Greeter Function example code folder. This application contains an HTML page with the output paragraph and a script section containing the greeter
function. Open the Developer View by pressing F12.
> greeter();
<- undefined
> function alerter() { alert("hello"); };
<- undefined
> alerter();
<- undefined
function m2(){ console.log("the"); } function m3(){ console.log("sat on"); m2(); }
function m1(){ m2(); console.log("cat"); m3(); console.log("mat"); }
> m1();
the cat sat on the mat
> function mirror() { mirror(); };
<- undefined
> mirror();
However, when a function calls itself, JavaScript repeatedly adds return addresses to the stack. Each time the function calls itself, another return address is added to the stack, at the top. The stack is finite in size, and when this size is exceeded, JavaScript stops the program.
Programmers have a name for a function that works by calling itself: recursion. Recursion is occasionally useful in programs, particularly when the program is searching for values in large data structures. However, I’ve been programming for many years and have used recursion only a handful of times. I advise you to regard recursion as strong magic that you don’t need to use now (or hardly ever). Loops are usually your best bet for repeating blocks of code.
Figure 7-1 shows the form of a JavaScript function definition. We can work through each of these items in turn. The keyword function
tells JavaScript that a function is being defined. JavaScript will allocate space for the function and get ready to start storing function statements. The word function
is followed by the identifier of the function. We must create an identifier for each function we define. Because a function is associated with an action, it’s a very good idea to make the name reflect this. I give functions names in the form verbNoun
. The verb specifies the action the function will perform, and the noun specifies the item it will work on. An example would be doMultiplicationTables
.
After the function identifier, we have the arguments that are fed into the function. The arguments are separated by commas and enclosed within parentheses. Arguments provide a function with something to work on. So far, the functions we’ve created haven’t had any arguments, so there has been nothing between the two parentheses. Finally, the definition contains a block of JavaScript statements that form the body of the function. These are the statements that will be obeyed when the function is called.
The greeter
function shows how functions can be used, but it isn’t really that useful because it does the same thing each time it’s called. To make a function truly useful, we need to give the function some data to work on. You’ve already seen many functions that are used in this way. The alert
function accepts a string to display in the alert box. The Number
function accepts an input to be turned into a numeric value. We could make a version of the greeter
function that accepts a message to be displayed:
function greeter(message) { var outputElement = document.getElementById("outputParagraph"); outputElement.textContent = message; }
CODE ANALYSIS
Investigating arguments
The new version of greeter
is in the example application Ch07 FunctionsCh07-02 Greeter Arguments. Open this application and then open the Developer View by pressing F12.
> greeter("Hello from Rob");
<- undefined
> greeter();
The JavaScript system notices that there is an argument missing from the call.
It supplies the function with the value undefined
for the missing argument.
The function sets the textContent
property of the output paragraph to undefined
. This is not displayed by the browser, so the text on the screen disappears.
function errorGreeter(message) { if(message == undefined) { alert("No argument to greeter"); } else { let outputElement = document.getElementById("outputParagraph"); outputElement.textContent = message; } }
> errorGreeter();
> greeter("Hello world", 1,2,3, "something else");
From the title of this section, you might expect that we will have a difference of opinion, but in JavaScript, the word argument has a particular meaning. In JavaScript, “argument” means “that thing you give to the call of a function.”
greeter("Hello world");
In the above statement, the argument is the string Hello world
. So when you hear the word “argument,” you should think of the code that is making a call of the function. In JavaScript, the word parameter means “the name within the function that represents the argument.” The parameters in a function are specified in the function definition.
function greeter(message)
This is the definition of a greeter
function that has a single parameter. The parameter has the identifier message
. When the function is called, the value of the message
parameter is set to whatever has been given as an argument to the function call. Statements within the function can use the parameter in the same way as they could use a variable with that name.
MAKE SOMETHING HAPPEN
Parameters as values
When a function is called, the value of the argument is passed into the function parameter. What exactly does this mean? The following program contains a function (with the interesting name whatWouldIDo
) that accepts a single parameter. The function doesn’t do much; it just sets the value of the parameter to 99
.
function whatWouldIDo(inputValue) { inputValue = 99; }
This function is part of the application in the Ch07 Creating functionsCh07-03 Parameters as values folder in the example code. Open this application and then open the Developer View by pressing F12. First, we are going to create a variable and put a value into it:
> var test=0;
When you press Enter, the console will create a variable called test
that contains the value 100
. We can use the test
variable as an argument to the whatWouldIDo
function:
> whatWouldIDo(test);
When the function has been called, the question we must consider is “What value does test
now contain?” Does it contain 0
(the value set when it was created), or does it contain 99
(the value set in the function whatWouldIDo
)? We can answer the question by asking the console to display the value of test
:
> test
<- 0
When a function is called, the value in the argument is copied into the parameter. So any changes to the parameter will not affect the argument at all.
A function can have multiple parameters. We might want a greeter
function that can display text in different colors. We could add a second parameter that contains a color name:
function colorGreeter(message, colorName) { var outputElement = document.getElementById("outputParagraph"); var elementColorStyle = "color:"+colorName; outputElement.textContent = message; outputElement.style=elementColorStyle; }
This function creates a style element using a supplied color name. The style is then applied to the output element along with the greeting text. We then provide arguments giving the string to be displayed and the color of the text:
colorGreeter("Hello in Red", "red");
WHAT COULD GO WRONG
Muddled arguments
The colorGreeter
function has two arguments that are mapped onto the two parameters that are used inside the function. The first argument has the greeting text, and the second argument has the name of the color to be used for the text. It is important that these arguments are supplied in the correct order.
colorGreeter("red", "Hello in Red");
This call of colorGreeter
has supplied the color name before the greeting text. In other words, they are the wrong way round. This would result in the colorGreeter
trying to display the message “red” in the color “Hello in Red.” This would not cause an error, but it would result in the program not doing what you want. So when we make calls to functions, we need to make sure that the arguments that are supplied are given in the same order as the parameters. You can find the colorGreeter
function in the Ch07 Creating functionsCh07-04 Color Greeter folder.
In a JavaScript program there are essentially two kinds of variable. There are variables that contain a value and variables that contain a reference to an object.
var age=12;
This statement creates a variable called age
, which contains a number with the value 12
.
var outputElement = document.getElementById("outputParagraph");
This statement creates a variable called outputElement
, which contains a reference to an HTML element in the page document. This is how our JavaScript programs have located the page elements to be used to display information for the user. We can pass references into function calls as arguments:
The HTML page above contains a function called makeGreen
. The function makeGreen
has a parameter called element
. The function sets the style of the element
parameter to the color green
. A program can use this function to make any given HTML element green.
makeGreen(outputElement);
MAKE SOMETHING HAPPEN
Reference arguments
The function makeGreen
is declared in the example application Ch07 Creating functionsCh07-05 Reference arguments folder in the example code. Open this application and then open the Developer View by pressing F12. First we are going to get a reference to the display paragraph.
> var outputElement = document.getElementById("outputParagraph");
When you press Enter, a variable called outputElement
is created that contains a reference to a paragraph on the screen. Now use this variable as an argument to a call of makeGreen
:
> makeGreen(outputElement);
This will cause the text of the paragraph on the web page to turn green because the makeGreen
function has set the style
property of the element to which it has received a reference:
What happens if we give makeGreen
the wrong kind of argument? Try this:
> makeGreen(21);
The result of this statement is that nothing happens. No error is produced even though it is not meaningful to try to set the style of a numeric value to 21. Any mistakes that you make with function arguments will not produce errors; instead, the program will just keep running.
There is another way that we can pass arguments into a JavaScript function—an array of items. We will discuss arrays in detail in the next chapter. An array is a form of collection. We have already seen one form of collection. The unordered list that we used to display ride information in the Theme Park in Chapter 6 held elements in the form of an array of items. We used the for–of
loop construction to work through the array. If you’re not sure about how this works, take a moment to return to Chapter 6 and read “Work through collections using for-of
.” Within a function, the keyword arguments
means the array of arguments that were given when the function was called.
The function calculateSum
shows how this works. It uses a for-of
loop to calculate the total of all the arguments given to the function call. This total value is then displayed on the page. The function can be called with any number of numeric arguments:
calculateSum(1,2,3,4,5,6,7,8,9,10);
This would display the value 55
. Of course, if you do something silly, you might not get what you expect.
calculateSum(1,2,3,"Fred","Jim","Banana");
This call of the function would not cause an error. It would display the result 6FredJimBanana
. This is because of the way that JavaScript combines strings and numbers. You can find out more in Chapter 4 in the section “Working with strings and numbers.” You can find this sample code in Ch07 Creating functionsCh07-06 Argument arrays.
A function can return a value. You have seen this in many of the programs we’ve written. Here’s an example:
var ageNo = Number(ageText);
This statement uses the Number
function. The function accepts an argument (text expressing a number) and returns a value (the text as a numeric value). We can write our own functions that return a value.
In Chapter 4, we created an application that displays random dice. Each time we wanted a random dice value, we had to perform some calculations. It would be useful to have these calculations in a function that we could use to get a random number of spots.
This is the function getDiceSpots
, which calculates a random number of spots. (Check out the section “Creating random dice” in Chapter 4 for details of how this works.) The last statement in the function uses the JavaScript return
keyword to return the calculated number to the caller. This function can be used whenever an application needs a dice value.
The function doRollDice
is called to display a dice value. It calls getDiceSpots
to get the value and then display it. You can see this in action in the example application Ch07 Creating functionsCh07-07 Returning values, which works in exactly the same way as the dice example in Ch04 Working with dataCh04-02 Computer Dice, but it uses the doRollDice
function.
Sometimes, a program needs a random number in a different range from the 1 to 6 provided by a die. We could use our newfound skills to make a function that accepts minimum and maximum values and then returns a random integer in that range.
The function getCustomDiceSpots
is given two parameters that specify the minimum and maximum values of the required random number. It works out the range of numbers that are required (the difference between the maximum and minimum values), calculates a random number in this range, and then adds that number to the minimum value to produce the number of spots. This number is then returned. One of the nice things about functions is that you don’t have to understand how they work to use them.
var health = getCustomDiceSpots(70,90);
The above statement creates a variable called health
, which contains a random number in the range 70 to 90. It could be used in a video game to set the initial health value of a monster that fights the player. Sometimes, the monster would be slightly harder to kill, which would make the game more interesting to play.
I’ve used the getCustomDiceSpots
function to make a dice application where the user can select the minimum and maximum values for random numbers that they want to use to play a particular game.
Figure 7-2 shows how the application is used. The user fills in the text boxes for the Min and Max values for the random number that they want and presses the Roll The Dice button to display a random number in that range.
The settings in Figure 7-2 will produce a value in the range 1 to 50 that could be used to play a bingo game. Let’s take a look at the code.
We have seen this kind of page before. There is some text to tell the user about the application. There are two input fields, which are used to get the minimum and maximum values for the random number to be produced. There is a Roll the Dice button to trigger the generation of the value and an output paragraph that displays the result. This page is very similar to the one used by the Theme Park Ride Selector application in Chapter 6. The function doRollDice
is called when the user clicks the button to roll the dice.
onclick="doRollDice('minNoText','maxNoText', 'outputParagraph');"
This is the onclick
attribute for the Roll the Dice button. This attribute contains a string of JavaScript code that will be obeyed when the button is clicked. If you’re not clear on how this works, take a look in the section “Using a button” in Chapter 2 for a refresh. The JavaScript that runs when the button is clicked makes a call of the function doRollDice
.
doRollDice('minNoText','maxNoText', 'outputParagraph');
The doRollDice
function has three arguments, which are the ID strings for the minNoText
, maxNoText
, and outputParagraph
elements in the web page. The function gets the minimum and maximum values from elements identified by the first two arguments to the function call. It then calculates the random result and then displays the result using the output element identified in the third element. We can take a look at this function to see how it works:
The function looks very small. It gets in the minimum and maximum values, calls getCustomDiceSpots
to get the random value, and then displays the result. The function is small because it uses another function called getNumberFromElement
to get the maximum and minimum values:
This function is given the identifier of an input element on the web page as a parameter. It fetches a number from the specified input element. We can use this function in any program that needs to read numbers from the user. It gets a reference to the element with the identifier that has been supplied as a parameter, gets the text from this element, converts the text into a number, and then returns the number to the caller. The function is called twice in the dice application—once to read the maximum value and once to read the minimum value. If I had an application that needed 10 inputs to be read from the screen, I could call this function 10 times in the application.
The Digital Dice program works well as long as we use it correctly. However, it is not without its problems. It is quite easy to enter invalid values for the minimum and maximum values. You can experiment with the application in the examples folder Ch07 Creating functionsCh07-08 Custom dice to discover what happens when it fails.
Figure 7-3 shows how the application can be used incorrectly. A minimum value of 50 and a maximum value of 2 is not correct. To make matters worse, the application has displayed a result that might be incorrect. We have no way of knowing if it has worked correctly because we are not sure how the getCustomDiceSpots
function behaves if it is given incorrect maximum and minimum values. It turns out that there are also other ways to cause the program problems. A user could press Roll The Dice when the Min and Max input areas are both empty. We need to fix all these things. Fortunately, we have the JavaScript skills that we need to sort this out.
One thing that separates a good programmer from a great programmer is the way that they handle errors. A great programmer will start a project by thinking of all the things that could go wrong when the user starts to use their program. It’s unlikely that they will think of every possible error (users can be very inventive when it comes to breaking things), but they will write down all the errors they can think of and then set out to deal with each error. Then, as the project continues, a great programmer will keep looking for possible errors and keep making sure that the errors that they know about are being handled correctly. In the case of the Digital Dice application, I can think of three things that might go wrong:
The user might not enter anything into the Max or Min inputs.
The user might enter a number outside the correct range for one of the Max or Min inputs.
The user might enter a Min value that is greater than or equal to the Max value.
Now that we have identified the errors, the next thing we need to decide is what the program should do when they are detected. If you are writing a program for a customer, you must discuss with them what is supposed to happen. Do they want the program to assume default values (perhaps a Min of 1 and a Max of 6), or do they want error messages to be displayed? Do they want the error to be a message on the screen or a pop-up alert? These are all important questions that the programmer alone can’t answer. The worst thing that can happen is that you make an assumption concerning what the customer wants. In my experience, making assumptions is a neat way of doubling your workload. You show the customer what you’ve made, they tell you it is not what they want, and you have to do it again. In the case of our customer, we have been told that the program should display a message and turn any invalid input areas on the screen red so that the user knows what to fix. So let’s see how we can use our JavaScript skills to solve this problem.
Let’s start with the error display. These are two style classes that I’m going to use to on the input elements on the web page. The menuInput
style is the “normal” style. The program will set the menuInputError
style to an input element that contains an invalid entry. Note that this has the background color set to red.
.menuInput { background-color: white; font-size: 1em; width: 2em; } .menuInputError { background-color: red; font-size: 1em; width: 2em; }
Our application uses a function called getNumberFromElement
to get a number from an input element. Let’s have a look at an improved function to deal with input errors:
CODE ANALYSIS
The getNumberFromElement
function
This function bears closer inspection. Here are answers to some questions about it:
<input class="menuInput" type="number" id="minNoText" value="1" min="1" max="99">
var max = Number(element.getAttribute("max")); var min = Number(element.getAttribute("min"));
if(result>max || result<min){ // fail because outside range element.className="menuInputError"; return NaN; }
These changes to the getNumberFromElement
function have addressed the first two errors that we identified. The user must now enter a valid number or the getNumberFromElement
function returns the value NaN
(not a number). Now the doRollDice
function needs to be updated. This version checks that the minimum and maximum values are both numbers. It also checks that the minimum value is less than the maximum value. If either of these conditions is not met, the function displays an alert.
Figure 7-4 shows the error handling in action. Note that I’ve also made another change to the application, so that it now shows the user the minimum and maximum values that are allowed for each input. This is a great way to reduce the chances of any errors in the first place.
Imagine several cooks working together in a kitchen. Each cook is working on a different recipe. The kitchen contains a limited number of pots and pans for the cooks to share. The cooks would need to coordinate so that two of them didn’t try to use the same pot. Otherwise, we might get sugar added to our soup and custard instead of gravy on our roast beef.
The designers of JavaScript faced a similar problem when creating functions. They didn’t want functions to fight over variables in the same way that two cooks might fight over a particular frying pan. You might think that it would be unlikely that two functions would try to use variables with the same name, but this is actually very likely.
Many programmers (including me) have an affection for the variable name i
, which they use for counting. If two functions use a variable called i
and one function calls the other function, this could lead to programs that don’t work properly because the second function might change i
to a value that the first function didn’t expect.
JavaScript solves this problem by providing a way for each function to have its own local variable space. This is the programming equivalent of giving each cook their own personal set of pots and pans. Any function can declare a local variable called i
that is specific to that function call. We declare a variable as local by using the keyword var
.
function fvar2() { var i=99; } function fvar1(){ var i=0; fvar2(); console.log("The value of i is:" + i); }
When a function returns, all local variables are destroyed. The code above shows two functions that contain local variables. Both fvar1
and fvar2
use a variable called i
. If we call f1var
, JavaScript follows this sequence:
The function fvar1
is called.
The first statement of fvar1
creates a variable called i
and sets it to 0
.
The second statement of fvar1
makes a call to fvar2
.
The first and only statement of fvar2
creates a variable called i
and sets it to 99
.
The function fvar2
finishes and control returns to the third statement of fvar1
.
The third statement of fvar1
prints out the value of i
.
The question we must consider is this: “What value is displayed in the console?” Is it the value 0 (which is set inside fvar1
), or is it 99 (which is set inside fvar2
)? If you’ve read the first part of this section, you know the value that will be printed is 0. The variables both have the same name (they are both called i
), but they each “live” in different functions.
JavaScript uses the word scope when talking about variable lifetimes. The scope of a variable is that part of a program in which a variable can be used. Each version of i
has a scope that is limited to the function body in which it is declared This form of isolation is called encapsulation. Encapsulation means that the operation of one function is isolated from the operation of other functions. Different programmers can work on different functions with no danger of problems being caused by variable names clashing with each other. Now take look at this pair of functions:
function f2() { i=99; } function f1(){ i=0; f2(); console.log("The value of i is:" + i); }
Can you spot the difference? These two functions don’t use the keyword var
to declare their variables. Is this important? Yes. It turns out that variables declared without the var
keyword are made global to the whole application, so f1
and f2
are now sharing a single global variable called i
. If we call the function f1
, it will print the value 99
because the call of f2
will change the value of the global variable i
. This means that you should always use var
to declare variables in your functions unless you specifically want to share them. In Chapter 4, in the section “Global and local variables,” we discovered a situation where a program needs to have a variable that has global scope. Now we are starting to understand how this works.
let
In Chapter 6, in the section “The JavaScript for
loop,” we discovered that we could declare local variables using the keyword let
. Variables declared using let
are discarded when a program leaves the block of statements where they were declared, whereas variables declared using var
are discarded when the program leaves the function in which they are declared.
The function letDemo
shows how this all works. It contains two versions of i
. The first version is declared as var
and exists for the entire function. The second version is declared inside a block and is discarded when the program leaves that block. Note that within the block it is not possible to use the outer version of i
because any references to i
will use the local version. In this case, we say that the outer variables is scoped out. The function also contains a variable called j
, which is declared inside the inner block. However, because j
is declared as var
, it can be used anywhere inside the function. These functions are declared in the example program in the Ch07 Creating functionsCh07-10 Variable scope folder.
If you find this confusing, then I apologize. The best way to understand it is to consider the problem that they are trying to solve (stopping lots of cooks fighting over the same pots and pans) and then work from there. The most important point is to always use var
or let
when you create a variable. Otherwise, you may find yourself a victim of very strange bugs in your programs.
MAKE SOMETHING HAPPEN
Double dice
Quite a few games use two dice rather than one. Make a version of the dice program that displays the results from two dice. You could even make this super-customizable so that the user can select the range of values to be produced by each die. If you are bit stuck with this, remember that there is nothing wrong with the onClick
behavior of a button calling two methods. And that two dice are just one dice (or die) times two. Take a look at my example solution in the examples at Ch07 Creating functionsCh07-11 Two Dice to discover the sneaky way I made the program work.
In this chapter, you learned how to take a block of code and turn it into a function that can be used from other parts of the program.
You’ve seen that a function contains a header that describes the function, and a block of code that is the body of the function. The function header supplies the name of the function and any parameters that are accepted by the function. When a function is called, the programmer supplies an argument that matches each parameter.
Parameters are items that the function can work on. They are passed by value, in that a copy is made of the argument given in the function call. If the function body contains statements that change the value of the parameter, this change is local to the function body. When an object (for example, a paragraph that has been obtained using the getElementById
method) is used as an argument to a function call, the function receives a reference to the object. This allows code in the function body to interact with attributes in the objects, as we saw with the makeGreen
function above.
A function can regard parameters as an array and can work through the arguments supplied to the function by using the arguments
keyword.
A function returns a single value. This is achieved by using the return
statement, which can be followed by a value to be returned. If no value is returned or the function does not perform a return statement, the function will return a special JavaScript value called undefined
, which is used to denote a missing value.
The scope of a variable in a program is that part of the program in which the variable can be used. Variables created inside the body of a function using the keyword var
have a scope that is local to the function and cannot be used by statements outside that function.
When creating software, it is important to consider potential errors at the start of the project rather than finding out about them later.
Here are some questions that you might like to ponder about the use of functions in programs:
Does using functions in programs slow down the program?
Not normally. There is a certain amount of work required to create the call of a function and then return from it, but this is not normally an issue. The benefits of functions far outweigh the performance issues.
Can I use functions to spread work around a group of programmers?
Indeed, you can. This is a very good reason to use functions. There are several ways that you can use functions to spread work around. One popular way is to write placeholder functions and build the application from them. A function will have the correct parameters and return value, but the body will do very little. As the program develops, programmers fill in and test each function in turn.
What is the maximum number of arguments that you can give to a function call?
We have seen that you can add lots of arguments to a call of a function. JavaScript does not place a limit on how many arguments you can pass in, but if you find yourself passing more than seven or eight, I’d advise you to think about how you are delivering the information to your function. If you want to pass a number of related items into a function, you might want to create an object (which we will explore in the next chapter) and pass in a reference to the object, rather than passing in lots of different values.
What is the difference between var
and let
when creating variables inside a function body?
Great question. Both keywords can be used to create a variable that exists within the block of code that is a function body. The difference between the two is that a variable created using var
will exist within the function body from that point onward. A variable created using let
will disappear at the end of the block in which it is created.
I’m still not clear on the difference between var
and let
in JavaScript. Why do we have them?
Remember that the problem that we are solving is that we don’t want a program to fail because programmers put values with different meanings into the same variable. If one part of a program used a variable called total
to hold the total sales and another part used a variable called total
to hold the total price, the program would fail. You would have the same problems if you used a single pan to make both custard and gravy while cooking a meal.
A variable created with the let
keyword only exists within the block where it is declared, which means that it cannot be affected by statements running outside that block. This would be like you grabbing a clean pan, making the custard, and then washing the pan and putting it away before it was used for anything else. The var
keyword allows you to create a variable that exists from the point of declaration and can be used to share a value around parts of a program or function.
Generally, you should create variables by using the let
keyword and only use var
if you really want the value in the variable to be used in other places in the function body. Note that a variable in a function that was created with the keyword var
will disappear when the program exits the function.
How do I come up with names for my functions?
The best functions have names given in a verb-noun form. getNumberFromElement
is a good name for a function. The first part indicates what it does, and the second part indicates where the value comes from. I find that thinking of function names (and variable names, for that matter) can be quite hard at times.
What do I do if I want to return more than one value from a function?
JavaScript functions can only return a single value. However, in the next chapter we will discover how to create data structures that can contain multiple values that can be returned by functions.
3.22.181.209