JS: How to Function?

When we released our jQuery.pin plugin at Webpop last week, part of the debate in Hackernews ended up being about the right way to define functions in JavaScript.

3 Ways

There's 3 different ways a named function can be defined in javascript:

The differences between these are subtle, and have to do with scoping.

You can think of function declarations as affecting the scope at compile time, while function expressions gets assigned to a variable at runtime.

Here's some examples of this difference you can try out:

If you paste this into your browsers web console, you'll see that even if f() is called before its definition, the code still works.

Lets try the same with a function expression:

When you run this in the inspector, you'll get: TypeError: undefined is not a function.

In the first case "f" is added to the scope at compile time and the function definition is ignored at runtime. In the second case, the function expression won't get bound to a variable at compile time, and we get an error since we're trying to treat an undefined variable as a function.

Lets try another variation of these examples, to see the difference:

Run this, and you'll see "Second f" in your console, even though the second f() is defined in an unreachable branch of the code.

The same example with a function expression:

This will log "First f" to your console when run, since the second function expression is never reached.

One place where this distinction gets way more obvious is if we try to define functions within a branch like this:

Here function declarations won't work. Even when a console is present, greet("Someone") will still give you an alert. Function expressions will work as expected:

In this case modern browsers will use the console for the greeting.

The Binding Identifier

The function expressions with binding identifier ads yet another twist to all of this.

In most ways it acts just like a function expression, but there's one subtle difference for recursive functions: the binding identifier can be used to reference the function from within the function body, but won't be declared in the scope the function is declared in:

If you try this in the inspector, you'll first see a countdown from 2 to 0 in the console when countdown(2) is called, and then "ReferenceError: count is not defined".

The Future: MOAR WAYS!

3 different ways to declare a function might seem like a lot, but choosing the right way is only going to get harder when ECMAScript 6 starts being an option. ECMAScript 6, the upcoming version of JavaScript, adds 2 new ways to declare functions:

Now, it might be tempting to think that this is just a bit of syntactic sugar for using function expressions, but no, no, no...

ArrowFunctions comes with their own subtle variation when it comes to scope. This time related to how this is treated:

You can try this in the web console of Firefox Nightly where arrow functions are now implemented. The result might surprise you: you'll see first "undefined" and then "42" in the console log.

This is a really tricky part of arrow functions: the usual dynamic this that we're used to from JavaScript functions is now a Lexical this. This gets really clear when we try to use call or apply with an arrow function:

This will output "42" and "huh" in the console of the current Firefox Nightly.

The funcitions "a" and "b" have the same function bodies and both "typeof a" and "typeof b" will return "function", but yet they behave quite differently when we use apply. The function defined as an arrow function simply ignores the scope argument and sticks to the lexical scope it was defined in.

Functions declared with the arrow syntax also can't be instantiated with "new" and don't have access to the special "arguments" object within their function body.

Wait! There's more!

So, with all these ways of defining functions, you might think we were done, but no, no, no...

The way arrow functions behave with regard to this means they're not suited to define methods on an object (where you would want this to point to the object).

To handle this ECMAScript 6 introduces yet another way of declaring a function: MethodDefinitions.

This is still not implemented in FireFox, so I haven't been able to play around with it, but here's how the syntax would look:

This ought to log "42" to your console if you could somehow run it.

 What to do?

So what's the right way to declare a function in JavaScript?

There's obviously no right answer to the question, but personally I tend to stick with function expressions.

As a language JavaScript has always been torn between it's C inspired syntax (that makes it look and feel like an imperative language) and it's Scheme-like qualities that allows for a much more functional style.

To me, it's more than anything the concept of first class functions that makes up for all of JavaScripts flaws and makes it a fun language to program in. The expression syntax seems to pronounce this quality of the language — always reminding you that you're just assigning new function objects to variables.

With arrow functions and method definitions it definitively seems like JavaScript is moving away from the function declaration approach. Arrow functions are more concise when writing in a functional style and method definitions seem like the obvious choice when writing in an object oriented style.

comments powered by Disqus