JavaScript: When is a function not a function?

Something that often confuses people new to JavaScript is the use of the function keyword. That’s because it represents two completely different operations, both called function.

First of all, there’s the lambda operator “function”. It works exactly like (lambda) in Scheme, {|| } in Ruby, or λ in lambda calculus. That is, it binds one or more arguments to an expression to be evaluated, forming what’s often referred to as a closure.

(An aside: In JavaScript it needn’t really be a closure, not least because JavaScript supports global variables, so the function may refer to values that are outside its lexical scope and not bound as arguments. In a true closure, this is not the case.)

So function (x,y) { return x + y; } binds the arguments x and y to the body code return x + y;. That binding gives you a closure, i.e. a function that has its own environment–but a function that’s anonymous, needing no name. If you apply that function to some values, the values go into x and y, the body code is evaluated, and the result is the result of that evaluation.

So (function (x,y) { return x + y; })(3, 4); gives us 7. The extra brackets around the (function ...) are just precedence brackets to make sure that the contents of those brackets get evaluated first, to give us the anonymous function, which we then apply to the arguments.

Notice that the function application is done exactly the same way as if you had a named function–by doing foo(args). It’s just that instead of “foo” being the name of some function we defined earlier, it’s an actual function constructed using the “function” lambda operator.

In JavaScript, functions are first-class objects; you can assign them to variables just like anything else. So we can assign our anonymous function to a variable, in order to give it a name:

var add = function (x,y) { return x + y; };

We can then use the name as a shorthand for the value (the function), when applying it to other values. So add(3,4) also gives 7.

Because the function has its own environment, we can also do things like this:

var addn = function (n) {
  return function (x) { return x + n; }
};

This function returns a function that adds n to a number. The value of n is stored in the environment of the function returned. So we can then do:

var add3 = addn(3);
alert(add3(4));

Now for the unnecessary confusion: As a shortcut for people used to other programming languages, JavaScript provides another thing called function, that sprinkles a little syntactic sugar on your function definitions:

function add(x,y) { return x + y; } is exactly the same as var add = function (x,y) { return x + y; };

Note that the syntactic sugar version doesn’t need a semicolon and tends not to be written with one, but the regular variable assignment version typically gets written like any other assignment statement, and usually requires the semicolon.

That is:
function add (x,y) { return x+y } function sub (x,y) { return x-y }
works, but
var add = function (x,y) { return x+y } var sub = function (x,y) { return x-y }
does not work.

This confuses the heck out of people. Sometimes function is followed by a name and has more brackets around it and has a semicolon, sometimes it isn’t, and they can’t understand why. It probably wouldn’t have been so bad if they had used (say) “lambda” for the lambda function and reserved “function” for the syntactic sugar. I tend to agree with Douglas Crockford (author of the excellent book “JavaScript: The Good Parts“) that the syntactic sugar was a mistake, and I always use the normal variable assignment version in my code now.

A second issue that confuses people is that JavaScript is lax about whitespace. It allows our lambda-based declaration to be written as var add = function(x,y) { return x + y; }; much as you can write for loops as for(...) rather than for (...)

This makes it look like you’re calling a function called “function” and giving it arguments–which you’re not. No code is being executed, you’re simply assigning a value to a variable, where the value happens to contain some code. So again, I try to follow Crockford’s recommendation and only use the syntactic form foo(x,y) when I’m actually executing a function foo and passing it values x and y. The rest of the time, I put a space before the opening bracket.

You might be wondering when you’d use anonymous lambda functions, rather than naming them. Basically, you use them whenever you need a function but don’t need to care what it’s called, or will only call it from one place. The most common cases are probably asynchronous AJAX callbacks and DOM events. Or if you write code like me, you use the lambda form whenever you need a function, and if you need it to have a name you explicitly give it a name using var.

[ Thanks to Tony Finch for comments on the first version of this item. ]

One thought on “JavaScript: When is a function not a function?

Comments are closed.