7.1 Defining and Invoking FunctionsAs we saw in Chapter 6, the most common way to define a function is with the function statement. This statement consists of the function keyword, followed by:
Example 7-1 shows the definitions of several functions. Although these functions are short and simple, they all contain each of the elements I just listed. Note that functions may be defined to expect varying numbers of arguments and that they may or may not contain a return statement. The return statement was introduced in Chapter 6; it causes the function to stop executing and to return the value of its expression (if any) to the caller. If a function does not contain a return statement, it simply executes each statement in the function body and returns the undefined value to the caller. Example 7-1. Defining JavaScript functions// A shortcut function, sometimes useful instead of document.write( ) // This function has no return statement, so it returns no value function print(msg) { document.write(msg, "<br>"); } // A function that computes and returns the distance between two points function distance(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; return Math.sqrt(dx*dx + dy*dy); } // A recursive function (one that calls itself) that computes factorials // Recall that x! is the product of x and all positive integers less than it function factorial(x) { if (x <= 1) return 1; return x * factorial(x-1); } Once a function has been defined, it may be invoked with the ( ) operator, introduced in Chapter 5. Recall that the parentheses appear after the name of the function and that an optional comma-separated list of argument values (or expressions) appears within the parentheses. The functions defined in Example 7-1 could be invoked with code like the following: print("Hello, " + name); print("Welcome to my home page!"); total_dist = distance(0,0,2,1) + distance(2,1,3,5); print("The probability of that is: " + factorial(39)/factorial(52)); When you invoke a function, each of the expressions you specify between the parentheses is evaluated and the resulting value is used as an argument of the function. These values are assigned to the parameters named when the function was defined, and the function operates on its parameters by referring to them by name. Note that these parameter variables are defined only while the function is being executed; they do not persist once the function returns. Since JavaScript is an untyped language, you are not expected to specify a data type for function parameters, and JavaScript does not check whether you have passed the type of data that the function expects. If the data type of an argument is important, you can test it yourself with the typeof operator. JavaScript does not check whether you have passed the correct number of arguments, either. If you pass more arguments than the function expects, the extra values are simply ignored. If you pass fewer than expected, some of the parameters are given the undefined value -- which, in many circumstances, causes your function to behave incorrectly. Later in this chapter, we'll see a technique you can use to test whether the correct number of arguments have been passed to a function. Note that the print( ) function does not contain a return statement, so it always returns the undefined value and cannot meaningfully be used as part of a larger expression. The distance( ) and factorial( ) functions, on the other hand, can be invoked as parts of larger expressions, as was shown in the previous examples. 7.1.1 Nested FunctionsECMAScript v1 and implementations prior to JavaScript 1.2 allow functions to be defined only in top-level global code. JavaScript 1.2 and ECMAScript v3, however, allow function definitions to be nested within other functions. For example: function hypotenuse(a, b) { function square(x) { return x*x; } return Math.sqrt(square(a) + square(b)); } Note that ECMAScript v3 does not allow function definitions to appear anywhere; they are still restricted to top-level global code and top-level function code. This means that function definitions may not appear within loops or conditionals, for example.[1] These restrictions on function definitions apply only to function declarations with the function statement. As we'll discuss later in this chapter, function literals (another feature introduced in JavaScript 1.2 and standardized by ECMAScript v3) may appear within any JavaScript expression, which means that they can appear within if and other statements.
7.1.2 The Function( ) ConstructorThe function statement is not the only way to define a new function. ECMAScript v1 and JavaScript 1.1 allow you to define a function dynamically with the Function( ) constructor and the new operator. (We saw the new operator in Chapter 5, and we'll learn more about constructors in Chapter 8.) Here is an example of creating a function in this way: var f = new Function("x", "y", "return x*y;"); This line of code creates a new function that is more or less equivalent to a function defined with the familiar syntax: function f(x, y) { return x*y; } The Function( ) constructor expects any number of string arguments. The last argument is the body of the function -- it can contain arbitrary JavaScript statements, separated from each other by semicolons. All other arguments to the constructor are strings that specify the names of the parameters to the function being defined. If you are defining a function that takes no arguments, you simply pass a single string -- the function body -- to the constructor. Notice that the Function( ) constructor is not passed any argument that specifies a name for the function it creates. The unnamed functions created with the Function( ) constructor are sometimes called anonymous functions. You might well wonder what the point of the Function( ) constructor is. Why not simply define all functions with the function statement? One reason is that Function( ) allows us to dynamically build and compile functions; it does not restrict us to the precompiled function bodies of the function statement. The flip side of this benefit is that the Function( ) constructor has to compile a function each time it is called. Therefore, you probably do not want to call this constructor within the body of a loop or within a frequently used function. Another reason to use the Function( ) constructor is that it is sometimes convenient, and even elegant, to be able to define a function as part of a JavaScript expression, rather than as a statement. We'll see examples of this usage later in this chapter. In JavaScript 1.2, when you want to define a function in an expression rather than a statement, a function literal is an even more elegant choice than the Function( ) constructor. We'll consider function literals next. 7.1.3 Function LiteralsECMAScript v3 defines and JavaScript 1.2 implements function literals, which are a third way to create functions. As discussed in Chapter 3, a function literal is an expression that defines an unnamed function. The syntax for a function literal is much like that of the function statement, except that it is used as an expression rather than as a statement and no function name is required. The following three lines of code define three more or less identical functions using the function statement, the Function( ) constructor, and a function literal: function f(x) { return x*x; } // function statement var f = new Function("x", "return x*x;"); // Function( ) constructor var f = function(x) { return x*x; }; // function literal Although function literals create unnamed functions, the syntax allows a function name to be optionally specified, which is useful when writing recursive functions that call themselves. For example: var f = function fact(x) { if (x <= 1) return 1; else return x*fact(x-1); }; This line of code defines an unnamed function and stores a reference to it in the variable f. It does not actually create a function named fact( ), but it does allow the body of the function to refer to itself using that name. Note, however, that this type of named function literal is not properly implemented before JavaScript 1.5. Keep in mind that the function statement is available in all versions of JavaScript, the Function( ) constructor is available only in JavaScript 1.1 and later, and function literals are available only in JavaScript 1.2 and later. Recall that we said the three functions defined earlier are "more or less" equivalent -- there are some differences between these three techniques for function definition, which we'll consider in Section 11.5. Function literals are useful in much the same way as functions created with the Function( ) constructor. Because they are created by JavaScript expressions rather than statements, they can be used in more flexible ways and are particularly suited for functions that are used only once and need not be named. For example, the function specified by a function literal expression can be stored into a variable, passed to another function, or even invoked directly: a[0] = function(x) { return x*x; }; // Define a function and store it a.sort(function(a,b){return a-b;}); // Define a function; pass it to another var tensquared = (function(x) {return x*x;})(10); // Define and invoke Like the Function( ) constructor, function literals create unnamed functions and do not automatically store those functions into properties. Function literals have an important advantage over the Function( ) constructor, however. The body of a function created by Function( ) must be specified in a string, and it can be awkward to express long, complex function bodies in this way. The body of a function literal, however, uses standard JavaScript syntax. Also, a function literal is parsed and compiled only once, while the JavaScript code passed as a string to the Function( ) constructor must be parsed and compiled each time the constructor is invoked. |