We’ve seen that functions are values in JavaScript programs. The
typeof
operator returns the string
“function” when applied to a function, but functions are really a
specialized kind of JavaScript object. Since functions are objects,
they can have properties and methods, just like any other object.
There is even a Function()
constructor to create new function objects. The subsections that
follow document function properties and methods and the Function()
constructor. You can also read
about these in the reference section.
The length Property
Within the body of a function, arguments.length
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 arity of the function—the number of
parameters it declares in its parameter list, which is usually the
number of arguments that the function expects.
The following code defines a function named check()
that is passed the arguments
array from another function. It
compares arguments.length
(the
number of arguments actually passed) to arguments.callee.length
(the number
expected) to determine whether the function was passed the right
number of arguments. If not, it throws an exception. The check()
function is followed by a test
function f()
that demonstrates
how check()
can be used:
// This function uses arguments.callee, so it won't work in strict mode.
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 differ.
throw Error("Expected " + expected + "args; got " + actual);
}
function f(x, y, z) {
check(arguments); // Check that the actual # of args matches expected #.
return x + y + z; // Now do the rest of the function normally.
}
The prototype Property
Every function has a prototype
property that refers to an
object known as the prototype object. Every
function has a different prototype object. When a function is used
as a constructor, the newly created object inherits properties from
the prototype object. Prototypes and the prototype
property were discussed in Prototypes and will be covered again in Chapter 9.
The call() and apply() Methods
call()
and apply()
allow you to indirectly invoke
(Indirect Invocation) a function as if it were a
method of some other object. (We used the call()
method in Example 6-4 to invoke Object.prototype.toString
on an object
whose class we wanted to determine, for example.) The first argument
to both call()
and apply()
is the object on which the
function is to be invoked; this argument is the invocation context
and becomes the value of the this
keyword within the body of the function. To invoke the function
f()
as a method of the object
o
(passing no arguments), you
could use either call()
or
apply()
:
f.call(o);
f.apply(o);
Either of the lines of code above are similar to the following
(which assume that o
does not
already have a property named m
):
o.m = f; // Make f a temporary method of o.
o.m(); // Invoke it, passing no arguments.
delete o.m; // Remove the temporary method.
In ECMAScript 5 strict mode the first argument to call()
or apply()
becomes the value of this
, even if it is a primitive value or
null
or undefined
. In ECMAScript 3 and non-strict
mode, a value of null
or undefined
is replaced with the global
object and a primitive value is replaced with the corresponding
wrapper object.
Any arguments to call()
after the first invocation context argument 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);
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]);
If a function is defined to accept an arbitrary number of
arguments, the apply()
method
allows you to invoke that function on the contents of an array of
arbitrary length. 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:
var biggest = Math.max.apply(Math, array_of_numbers);
Note that apply()
works
with array-like objects as well as true arrays. In particular, you
can invoke a function with the same arguments as the current
function by passing the arguments
array directly to apply()
. The
following code demonstrates:
// Replace the method named m of the object o with a version that logs
// messages before and after invoking the original method.
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message.
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
This trace()
function is
passed an object and a method name. It replaces the specified method
with a new method that “wraps” additional functionality around the
original method. This kind of dynamic alteration of existing methods
is sometimes called “monkey-patching.”
The bind() Method
The bind()
method was added
in ECMAScript 5, but it is easy to simulate in ECMAScript 3. As its name implies, the
primary purpose of bind()
is to
bind a function to an object. When you invoke the bind()
method on a function f
and pass an object o
, the method returns a new function.
Invoking the new function (as a function) invokes the original
function f
as a method of
o
. Any arguments you pass to the
new function are passed to the original function. For
example:
function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 }; // An object we'll bind to
var g = f.bind(o); // Calling g(x) invokes o.f(x)
g(2) // => 3
It is easy to accomplish this kind of binding with code like
the following:
// Return a function that invokes f as a method of o, passing all its arguments.
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
The ECMAScript 5 bind()
method does more than just bind a function to an object. It also
performs partial application: any arguments you pass to bind()
after the first are bound along
with the this
value. Partial
application is a common technique in functional programming and is
sometimes called currying. Here are some
examples of the bind()
method
used for partial application:
var sum = function(x,y) { return x + y }; // Return the sum of 2 args
// Create a new function like sum, but with the this value bound to null
// and the 1st argument bound to 1. This new function expects just one arg.
var succ = sum.bind(null, 1);
succ(2) // => 3: x is bound to 1, and we pass 2 for the y argument
function f(y,z) { return this.x + y + z }; // Another function that adds
var g = f.bind({x:1}, 2); // Bind this and y
g(3) // => 6: this.x is bound to 1, y is bound to 2 and z is 3
We can bind the this
value
and perform partial application in ECMAScript 3. The standard
bind()
method can be simulated
with code like that shown in Example 8-5. Note that
we save this method as Function.prototype.bind
, so that all
function objects inherit it. This technique is explained in detail
in Augmenting Classes.
Example 8-5. A Function.bind() method for ECMAScript 3
if (!Function.prototype.bind) {
Function.prototype.bind = function(o /*, args */) {
// Save the this and arguments values into variables so we can
// use them in the nested function below.
var self = this, boundArgs = arguments;
// The return value of the bind() method is a function
return function() {
// Build up an argument list, starting with any args passed
// to bind after the first one, and follow those with all args
// passed to this function.
var args = [], i;
for(i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]);
for(i = 0; i < arguments.length; i++) args.push(arguments[i]);
// Now invoke self as a method of o, with those arguments
return self.apply(o, args);
};
};
}
Notice that the function returned by this bind()
method is a closure that uses the
variables self
and boundArgs
declared in the outer function,
even though that inner function has been returned from the outer
function and is invoked after the outer function has
returned.
The bind()
method defined
by ECMAScript 5 does have some features that cannot be simulated
with the ECMAScript 3 code shown above. First, the true bind()
method returns a function object
with its length
property properly
set to the arity of the bound function minus the number of bound
arguments (but not less than zero). Second, the ECMAScript 5
bind()
method can be used for
partial application of constructor functions. If the function
returned by bind()
is used as a
constructor, the this
passed to
bind()
is ignored, and the
original function is invoked as a constructor, with some arguments
already bound. Functions returned by the bind()
method do not have a prototype
property (the prototype
property of regular functions
cannot be deleted) and objects created when these bound functions
are used as constructors inherit from the prototype
of the original, unbound
constructor. Also, a bound constructor works just like the unbound
constructor for the purposes of the instanceof
operator.
The toString() Method
Like all JavaScript objects, functions have a toString()
method. The ECMAScript spec
requires this method to return a string that follows the syntax of
the function declaration statement. In practice most (but not all)
implementations of this toString()
method return the complete
source code for the function. Built-in functions typically return a
string that includes something like “[native code]” as the function
body.
The Function() Constructor
Functions are usually defined using the function
keyword, either in the form of a
function definition statement or a function literal expression. But
functions can also be defined with the Function()
constructor. For
example:
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:
var f = function(x, y) { return x*y; }
The Function()
constructor
expects any number of string arguments. The last argument is the
text of the function body; it can contain arbitrary JavaScript
statements, separated from each other by semicolons. All other
arguments to the constructor are strings that specify the parameters
names for the function. 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. Like function literals, the Function()
constructor creates anonymous
functions.
There are a few points that are important to understand about
the Function()
constructor:
-
The
Function()
constructor allows JavaScript functions to be dynamically
created and compiled at runtime. -
The
Function()
constructor parses the function body and creates a new function
object each time it is called. If the call to the constructor
appears within a loop or within a frequently called function,
this process can be inefficient. By contrast, nested functions
and function definition expressions that appear within loops are
not recompiled each time they are encountered. -
A last, very important point about the
Function()
constructor is that the
functions it creates do not use lexical scoping; instead, they
are always compiled as if they were top-level functions, as the
following code demonstrates:var scope = "global"; function constructFunction() { var scope = "local"; return new Function("return scope"); // Does not capture the local scope! } // This line returns "global" because the function returned by the // Function() constructor does not use the local scope. constructFunction()(); // => "global"
The Function()
constructor
is best thought of as a globally-scoped version of eval()
(see Global eval())
that defines new variables and functions in its own private scope.
You should rarely need to use this constructor in your code.
Callable Objects
We learned in Array-Like Objects that there are
“array-like” objects that are not true arrays but can be treated
like arrays for most purposes. A similar situation exists for
functions. A callable object is any object that
can be invoked in a function invocation expression. All functions
are callable, but not all callable objects are functions.
Callable objects that are not functions are encountered in two
situations in today’s JavaScript implementations. First, the IE web
browser (version 8 and before) implements client-side methods such
as Window.alert()
and Document.getElementsById()
using callable
host objects rather than native Function objects. These methods work
the same in IE as they do in other browsers, but they are not
actually Function objects. IE9 switches to using true functions, so
this kind of callable object will gradually become less
common.
The other common form of callable objects are RegExp
objects—in many browsers, you can invoke a RegExp object directly as
a shortcut for invoking its exec()
method. This is a completely
nonstandard feature of JavaScript that was introduced by Netscape
and copied by other vendors for compatibility. Do not write code
that relies on the callability of RegExp objects: this feature is
likely to be deprecated and removed in the future. The typeof
operator is not interoperable for
callable RegExps. In some browsers it returns “function” and in
others it returns “object”.
If you want to determine whether an object is a true function
object (and has function methods) you can test its
class attribute (The class Attribute) using the technique shown in Example 6-4:
function isFunction(x) {
return Object.prototype.toString.call(x) === "[object Function]";
}
Note that this isFunction()
function is quite similar to the isArray()
function shown in Array Type.