loading...

JavaScript – Functions As Values

The most important features of functions are that they can be
defined and invoked. Function definition and invocation are syntactic
features of JavaScript and of most other programming languages. In
JavaScript, however, functions are not only syntax but also values,
which means they can be assigned to variables, stored in the
properties of objects or the elements of arrays, passed as arguments
to functions, and so on.[12]

To understand how functions can be JavaScript data as well as
JavaScript syntax, consider this function definition:

function square(x) { return x*x; }

This definition creates a new function object and assigns it to
the variable square. The name of a
function is really immaterial; it is simply the name of a variable
that refers to the function object. The function can be assigned to
another variable and still work the same way:

var s = square;     // Now s refers to the same function that square does
square(4);          // => 16
s(4);               // => 16

Functions can also be assigned to object properties rather than
variables. When you do this, they’re called methods:

var o = {square: function(x) { return x*x; }}; // An object literal
var y = o.square(16);                          // y equals 256

Functions don’t even require names at all, as when they’re
assigned to array elements:

var a = [function(x) { return x*x; }, 20];   // An array literal
a[0](a[1]);                                  // => 400

The syntax of this last example looks strange, but it is still a
legal function invocation expression!

Example 8-2 demonstrates the kinds of
things that can be done when functions are used as values. This
example may be a little tricky, but the comments explain what is going
on.

Example 8-2. Using functions as data

// We define some simple functions here
function add(x,y) { return x + y; }
function subtract(x,y) { return x - y; }
function multiply(x,y) { return x * y; }
function divide(x,y) { return x / y; }

// Here's a function that takes one of the above functions
// as an argument and invokes it on two operands
function operate(operator, operand1, operand2) {
    return operator(operand1, operand2);
}

// We could invoke this function like this to compute the value (2+3) + (4*5):
var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));

// For the sake of the example, we implement the simple functions again, 
// this time using function literals within an object literal;
var operators = {
    add:      function(x,y) { return x+y; },
    subtract: function(x,y) { return x-y; },
    multiply: function(x,y) { return x*y; },
    divide:   function(x,y) { return x/y; },
    pow:      Math.pow  // Works for predefined functions too
};

// This function takes the name of an operator, looks up that operator
// in the object, and then invokes it on the supplied operands. Note
// the syntax used to invoke the operator function.
function operate2(operation, operand1, operand2) {
    if (typeof operators[operation] === "function")
        return operators[operation](operand1, operand2);
    else throw "unknown operator";
}

// Compute the value ("hello" + " " + "world") like this:
var j = operate2("add", "hello", operate2("add", " ", "world"));
// Using the predefined Math.pow() function:
var k = operate2("pow", 10, 2);

As another example of functions as values, consider the Array.sort() method. This method sorts the
elements of an array. Because there are many possible orders to sort
by (numerical order, alphabetical order, date order, ascending,
descending, and so on), the sort()
method optionally takes a function as an argument to tell it how to
perform the sort. This function has a simple job: for any two values
it is passed, it returns a value that specifies which element would
come first in a sorted array. This function argument makes Array.sort() perfectly general and
infinitely flexible; it can sort any type of data into any conceivable
order. Examples are shown in sort().

Defining Your Own Function Properties

Functions are not primitive values in JavaScript, but a
specialized kind of object, which means that functions can have
properties. When a function needs a “static” variable whose value
persists across invocations, it is often convenient to use a
property of the function, instead of cluttering up the namespace by
defining a global variable. For example, suppose you want to write a
function that returns a unique integer 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. You 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:

// Initialize the counter property of the function object.
// Function declarations are hoisted so we really can 
// do this assignment before the function declaration.
uniqueInteger.counter = 0;

// This function returns a different integer each time it is called.
// It uses a property of itself to remember the next value to be returned.
function uniqueInteger() {
    return uniqueInteger.counter++;  // Increment and return counter property
}

As another example, consider the following factorial() function that uses properties
of itself (treating itself as an array) to cache previously computed
results:

// Compute factorials and cache results as properties of the function itself.
function factorial(n) {
    if (isFinite(n) && n>0 && n==Math.round(n)) { // Finite, positive ints only
        if (!(n in factorial))                    // If no cached result
            factorial[n] = n * factorial(n-1);    // Compute and cache it
        return factorial[n];                      // Return the cached result
    }
    else return NaN;                              // If input was bad
}
factorial[1] = 1;  // Initialize the cache to hold this base case.


[12] This may not seem like a particularly interesting point
unless you are familiar with languages such as Java, in which
functions are part of a program but cannot be manipulated by the
program.

Comments are closed.

loading...