We've seen that functions can be used as data values in JavaScript programs and that they can be created with the Function( ) constructor. These are sure signs that functions are actually represented by a type of JavaScript object, the Function object. Since functions are objects, they have properties and methods, just like the String and Date objects, for example. Now that we've discussed the call and Arguments objects that are used in the context of function invocation, let's turn to the Function object itself.
As we've seen, within the body of a function, the length property of the arguments array specifies the number of arguments that were passed to the function. The length property of a function itself, however, has a different meaning. This read-only property returns the number of arguments that the function expects to be passed -- that is, the number of parameters it declares in its parameter list. Recall that a function can be invoked with any number of arguments, which it can retrieve through the arguments array, regardless of the number of parameters it declares. The length property of the Function object specifies exactly how many declared parameters a function has. Note that unlike arguments.length, this length property is available both inside and outside of the function body.
The following code defines a function named check( ) that is passed the arguments array from another function. It compares the arguments.length property to the Function.length property (which it accesses as arguments.callee.length) to see if the function was passed the number of arguments it expected. If not, it throws an exception. The check( ) function is followed by a test function f( ) that demonstrates how check( ) can be used:
function check(args) { var actual = args.length; // The actual number of arguments var expected = args.callee.length; // The expected number of arguments if (actual != expected) { // Throw an exception if they don't match throw new Error("Wrong number of arguments: expected: " + expected + "; actually passed " + actual); } } function f(x, y, z) { // Check that the actual # of args matches the expected # of args // Throw an exception if they don't match check(arguments); // Now do the rest of the function normally return x + y + z; }
The length property of the Function object is standardized by ECMAScript v1 and implemented in JavaScript 1.1 and later.[25]
[25]In Netscape 4.0, a bug prevents this property from working correctly unless the language attribute of the <script> tag is explicitly set to "JavaScript1.2".
Every function has a prototype property that refers to a predefined prototype object. This prototype object comes into play when the function is used as a constructor with the new operator; it plays an important role in the process of defining new object types. We'll explore this property in detail in Chapter 8.
When a function needs to use a variable whose value persists across invocations, it is often convenient to use a property of the Function object, instead of cluttering up the namespace by defining a global variable. For example, suppose we want to write a function that returns a unique identifier whenever it is invoked. The function must never return the same value twice. In order to manage this, the function needs to keep track of the values it has already returned, and this information must persist across function invocations. We could store this information in a global variable, but that is unnecessary because the information is used only by the function itself. It is better to store the information in a property of the Function object. Here is an example that returns a unique integer whenever it is called:
// Create and initialize the "static" variable. // Function declarations are processed before code is executed, so // we really can do this assignment before the function declaration. uniqueInteger.counter = 0; // Here's the function. It returns a different value each time // it is called and uses a "static" property of itself to keep track // of the last value it returned. function uniqueInteger( ) { // Increment and return our "static" variable return uniqueInteger.counter++; }
ECMAScript v3 defines two methods that are defined for all functions, call( ) and apply( ). These methods allow you to invoke a function as if it were a method of some other object. (Note that we have not discussed methods yet; you may find this section more understandable once you have read Chapter 8.) The first argument to both call( ) and apply( ) is the object on which the function is to be invoked; this argument becomes the value of the this keyword within the body of the function. Any remaining arguments to call( ) are the values that are passed to the function that is invoked. For example, to pass two numbers to the function f( ) and invoke it as if it were a method of the object o, you could use code like this:
f.call(o, 1, 2);
This is similar to the following lines of code:
o.m = f; o.m(1,2); delete o.m;
The apply( ) method is like the call( ) method, except that the arguments to be passed to the function are specified as an array:
f.apply(o, [1,2]);
For example, to find the largest number in an array of numbers, you could use the apply( ) method to pass the elements of the array to the Math.max( ) function:[26]
[26]This example assumes we are using the ECMAScript v3 Math.max( ) function, which accepts an arbitrary number of arguments; the ECMAScript v1 version of the function accepts only two arguments.
var biggest = Math.max.apply(null, array_of_numbers);
The apply( ) method is implemented in JavaScript 1.2, but the call( ) method is not implemented until JavaScript 1.5.
Copyright © 2003 O'Reilly & Associates. All rights reserved.