loading...

JavaScript – Function Properties, Methods, and Constructor

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.

Comments are closed.

loading...