JavaScript isn’t the first language to use first class functions, but it’s likely the language that made them popular. Java, Objective C, C#, C++, Python… these are all languages that did not have first class functions until recently. Once JavaScript became one of the world’s most popular programming languages, other language designers realized just how useful first class functions really are, and have added first class functions to the languages mentioned above, as well as newer languages like Swift and Go.
If you recall from Chapter 10 of Head First JavaScript Programming, a first class value is one that can be assigned to a variable, passed as an argument, and returned from a function. In JavaScript, functions are values, and pass all three criteria for first class status in the language.
What’s the big deal? Well, think about how you typically create a timer event:
setTimeout(function() { alert("Cookies are ready!"); }, 1000);
setTimeout
takes two arguments: a function to be executed once the timer expires, and the time to wait before calling the function (in milliseconds). setTimeout
takes care of calling the function once the 1000ms has passed. Being able to pass any function you like to setTimeout
makes this code flexible, concise, and easy to understand.
Let’s take a look at another common use case for first class functions:
var even = []; [1, 2, 3].forEach(function(x) { even.push(x * 2); });
This code uses the array method forEach
to double the values in an array. The resulting array, even
, has the value: [2, 4, 6]
. forEach
takes a function as an argument, and applies that function to each value in the array. It’s a short and easy way to write a loop on an array.
Once you start programming in JavaScript, you’ll find first class functions everywhere. Currently, in JavaScript ES5 (the version implemented in most browsers today), we write all functions in the same way; once ES6 is implemented in browsers, you’ll likely start seeing arrow functions replace the functions you’re used to in many use cases like the ones we’ve seen above.
Arrow functions
So what is an arrow function? It’s a function, but differs from functions as you know them in JavaScript ES5 in two important ways. First, you write an arrow function using different syntax than you do for a regular function. An arrow function to add 2 to a value looks like this:
(x) => x + 2
Let’s take a look at what this means. Start with a regular function that does the same thing:
function add2(x) { return x + 2; }
Then, remove the function
keyword and the name of the function, add2
:
(x) { return x + 2; }
Now, add the arrow:
(x) => { return x + 2; }
You would keep the curly braces and the return if you had more than one statement in the function body, but because we have only one statement that returns the value of an expression, we can get rid of both the curly braces and the return:
(x) => x + 2
So, what’s on the left of the arrow is a list of parameters (much like the list of parameters that typically follows the function
keyword in a regular function). And on the right of the arrow, in this case, is an expression that is evaluated when the function is called, and returns the result of the expression. The arrow function doesn’t have a name because arrow functions are designed to be used in place of anonymous functions.
Compare the original function with the arrow function and you can see that the arrow function is a lot more concise.
Let’s try using this function now (Firefox 38; arrow functions do not yet work in other browsers as of this writing):
function apply(f, n) { return f(n); } apply((x) => x + 2, 10);
The call to apply
results in the value 12.
apply
simply takes a function and a (number) value and calls the function, passing the number to the function. We can pass the arrow function we created to apply
in exactly the same way as we’d pass a regular function.
Arrow functions are designed to be used whenever you would typically pass a short, anonymous function to another function (or, likewise, return a short, anonymous function from another function). With that in mind, let’s rewrite the previous two examples using arrow functions:
setTimeout(() => { alert("Cookies are ready!"); }, 1000); var even = []; [1, 2, 3].forEach((x) => { even.push(x * 2); });
Here, because we’re not returning a value from either of these functions, I use the curly braces to delimit the statement in the function body in both examples, even though it’s not strictly necessary (the functions can return undefined since we’re not relying on the return values). But any time you have multiple statements in the body of an arrow function you must use the curly braces, e.g.:
var numberToGuess = 3; setTimeout(() => { var guess = Math.floor(Math.random() * 10); if (guess == numberToGuess) { alert("You won!"); } else { alert("Sorry, better luck next time."); } }, 1000);
Here, we’re passing an arrow function to setTimeout
that generates a random number and compares it to the variable numberToGuess
. If the values are the same, you won, otherwise, better luck next time. Here, the arrow function behaves just like a regular function would; you just get to save a little bit of code by not writing the keyword function
.
Okay, so you save a little bit of typing. Big deal. Sometimes it’s handy (particularly if your arrow functions are super short and just return a value, like we saw above with the add2
example).
But there’s another important difference between arrow functions and regular functions that will be very useful in some circumstances. And that is: arrow functions are pure functions—they are never methods. And that means that the way the value of this
is handled in an arrow function is different from the way this
is handled in a regular function. Let’s see how, next.
The value of this
Here, we have an object, beatBox
, with a method play
that takes an array of notes (strings) and plays the sample sound (“gong”) for each note. Our initial implementation of play
uses a for
loop to loop through all the notes in the sequence:
var beatBox = { sample: "gong", play: function(sequence) { for (var i = 0; i < sequence.length; i++) { var note = sequence[i]; console.log("Play " + this.sample + " for note ", note); } } }; beatBox.play(['A', 'C', 'F']);
This works fine. Notice that in this example, the value of this
in the body of the play
function is the object whose method we called; that is, beatBox
. So when we refer to this.sample
, we’re referring to the sample
property of the beatBox
object.
Now, let’s say you decide to replace the for
loop in the code above with the forEach
method on your sequence
array:
var beatBox = { sample: "gong", play: function(sequence) { sequence.forEach(function(note) { console.log("Play " + this.sample + " for note ", note); }); } }; beatBox.play(['A', 'C', 'F']);
When you try to run this code, you’ll see the following output:
Play undefined for note A Play undefined for note C Play undefined for note F
The code this.sample
is no longer working. That’s because of the way that JavaScript handles the value of this
in functions that aren’t methods. We are passing a function to the forEach
method of the array, and in that function, the value of this
is the global object, window
. (Try adding a log to the console inside the function to see!).
This is just the way that JavaScript works. When you have a function that’s not being called as a method, then, by default, that function is actually a method of the window
object (or whatever the global object is, depending on your JavaScript environment). So, it’s as if the forEach
method is calling window.function. And so, because this function is actually a method of window
, the value of this
is the window
object!
We lose the value of this
when we call another function inside the method play
.
This issue has long plagued JavaScript programmers in precisely this situation. We have a couple of workarounds; a common pattern is to save the value of this
in a variable defined where this
is the object we want it to be (that is, in a context outside the function where its value is lost):
var beatBox = { sample: "gong", play: function(sequence) { var that = this; sequence.forEach(function(note) { console.log("Play " + that.sample + " for note ", note); }); } }; beatBox.play(['A', 'C', 'F']);
Try this code, and you’ll find it runs fine. What we’re doing here is saving the value of this
in the variable that
before we call the forEach
method on the array (this value is the beatBox
object). Then, we use that.sample
instead of this.sample
in the body of the function we pass to forEach
. Notice that to find the value of that
in the body of the function we’re passing to forEach
, we use lexical scoping: we look first in the function itself to see if that
is defined; it’s not, so we then look in the context surrounding the function (the play
method), and it is, so that’s the value we use for that
(the beatBox
object).
(For a refresher on lexical scoping, refer to Chapter 11 of Head First JavaScript Programming).
Another common solution is to use bind
:
var beatBox = { sample: "gong", play: function(sequence) { sequence.forEach(function(note) { console.log("Play " + this.sample + " for note ", note); }.bind(this)); } }; beatBox.play(['A', 'C', 'F']);
bind
is a function method: you call bind
on a function object, and pass in the object you want to use as this
in the body of the function. bind
returns a new function with this
bound to the value you passed in.
(For more on how bind
works, check out the Head First JavaScript Programming Extra I wrote on this topic).
Here, we’re passing this
, the beatBox
object, to use as the value for this
in the body of the function we’re passing to the forEach
array method. That means when we write this.sample
, we get the property from the beatBox
object, just as we wanted.
With arrow functions, we no longer need either of these solutions.
var beatBox = { sample: "gong", play: function(sequence) { sequence.forEach((note) => { console.log("Play " + this.sample + " for note ", note); }); } }; beatBox.play(['A', 'C', 'F']);
Here, we’ve replaced the function we’re passing to the forEach
method with an arrow function. Remember that an arrow function is never called as a method; it’s a pure function, unlike when we used a regular function, which ends up being called as a method of the window
object.
So how does JavaScript know what value to use for this
in the body of the arrow function we’re passing to forEach
? We use lexical scoping, just like we’d use to determine the value of any other unbound variable in the body of a function.
Using lexical scoping, we first look for a value for this
defined in the arrow function. There isn’t one, so we look for a value for this
defined in the context surrounding the arrow function, and we do find one: in the function play
(which is the context surrounding the arrow function), this
is defined to be the beatBox
object. So that’s the value we use for this
in the arrow function.
Try running this code in Firefox, and you’ll see that it works just fine.
Summary
So arrow functions differ from regular functions in two important ways: syntax, and how the value of this
is determined. Syntax is easy to learn, but remembering that JavaScript now has two different ways of treating this
in functions is a little trickier. It will take a little bit of time to get the hang of arrow functions in situations where you’re using this
.
Note that this
can never be set for arrow functions. Unlike a regular function where we can set the value of this
using bind
(like we did above), you can’t use bind
(or call
) with arrow functions to set the value of this
. You can only use bind
and call
to set the values of arguments (in other words, the first argument to bind
is just ignored). An arrow function will always determine the value of this
using lexical scoping.
Now you might be thinking: there are many situations where I could use either a regular function or an arrow function and it wouldn’t make any difference (e.g. our add2
function above). So which is better?
It really depends on the situation. Both arrow functions and regular functions have their place, particularly if you are concerned about the value of this
. Arrow functions can sometimes be considerably shorter than regular functions to write (although not always). So, it’s really up to you which one you use.
The key thing to know is that now JavaScript has two different kinds of functions: regular functions, which are always called as methods of objects—explicitly, or implicitly with the window
object—, and arrow functions, which are never called as methods of objects.
There’s one more use case for functions we haven’t looked at yet, and that is how we use functions to create new objects. I’ll talk about how that’s changing in ES6 in my next blog post.
Resources
- Chapter Eleven of Head First JavaScript Programming
- ECMAScript 6 compatibility table
- Mozilla’s copy of the working draft of the ECMAScript 6 specification
- Traceur transpiler which converts ES6 code into ES5 code for some features (not all)
- If you need a refresher on how to use the browser console, read this post