JavaScript functions: First-class objects

In JavaScript, functions are first-class objects, meaning that they can be created, manipulated and passed around in the same manner as other objects/variables in JavaScript. For example, a function can be created, stored in a variable or even be the return value of another function, as seen below:

function getPower(power)
{
  return function(x)
  {
    return Math.pow(x, power);
  }
}

var x3 = getPower(3);
window.alert(x3(3)); // Outputs 27.

In the rather stupid and contrived example above, we make a function getPower() that returns another function which raises the given value to the exponent supplied by calling getPower(). (This is a bad way to do things for numerous reasons, but is just shown for the sake of providing a simple example)

We then call getPower with a power of 3 and assign the returned function to the variable x3, and the output is as expected. Defining “inline” functions this way and manipulating them is closely associated with the concept of anonymous functions.

Functions: Copied and passed by reference

Since functions are objects, and objects are passed and copied by reference, the behaviour should be fairly straightforward to those familiar with the concept. Here’s a quick example of what I’m talking about. Since JavaScript functions can be treated just like plain old objects, this means we can attach arbitrary properties to them – let’s see how that relates to making a “copy” of a function:

function test() {window.alert("A Test");}
f = window.test;
window.test.aProperty = 'Hello!';
window.alert(f.aProperty); // Outputs "Hello!"

f.aProperty = 'Goodbye!';
window.alert(window.test.aProperty); // Outputs "Goodbye!"

The key here is that the expression f = window.test; doesn’t make a complete copy of the function; instead it just ensures that the variable f will point at the same function object as window.test. So expressions that modify the underlying function object and its data will reflect in both window.test and f. Just think of those two variables as being different ways of accessing the same underlying data.

But let’s consider another example: What happens if we make a copy of a function and then redefine the original?

function test() {window.alert("A Test");}

f = window.test;
f(); // "A Test"

// Redefine the original function.
test = function() {window.alert("A changed test");};

f(); // Still "A Test"!
window.test(); // "A changed test"

The results are a bit strange – it appears that when we redefine test, the changes are not reflected in the copy we created in variable f! Why is this?

Closer inspection yields the following answer: We were not actually redefining the function pointed to by test. Instead, we created a new Function object in memory and then “pointed” test at this new function. The old function, formerly referenced by test, is still referenced by the variable f so that is why it continues to invoke that code.

This is illustrated by the following diagrams. In the first, the original function has been defined and two variables refer to it.

javascript-function-1

In the second diagram, we have created a new function and altered the variable test to refer to it; however the variable f still refers to the original function. Thus, the important thing to note is that when using the assignment operator for functions, they are copied by reference.

javascript-function-2
The original function is still referenced by f

Where this matters

This point has relevance when talking about event handlers. Typically, when we bind functions to a specific events this involves copying a function reference over to some other variable or property. Whether we directly do this using traditional event registration by using an expression like element.onclick = someFunction or whether it’s done using jQuery’s Event Helpers, the effect is the same.

This means that after assigning the event handler, we cannot simply modify the original function to make changes to how the event handler works. This is because when we assign a new function the old one will still be referenced by the event handler. The proper way to do this at runtime would be simply to register the new function to the event and deregister the old one.

Another way to think of it is that you can often register anonymous functions to events; since they are anonymous you won’t have a reference to them after you assign them to the event handler so there is no way to modify them after the fact. This same logic applies equally when assigning non-anonymous functions to events.

Comments for this entry are closed

But feel free to indulge in some introspective thought.