One of the biggest new features in the ES2015 specification was the introduction of arrow functions. You have probably already seen these around the internet, and indeed in my previous blog posts. They help to make function definitions much shorter, as well as making working with this
much easier.
Shorter function definitions
Let’s start with the basics. How do you define an arrow function? In the simplest case, remove the function
keyword, and add =>
after the list of arguments. In the example below, these functions do the same thing, adding x and y together.
let addFn1 = function(x, y) {
return x + y;
}
let addFn2 = (x, y) => {
return x + y;
}
But actually, we can write our arrow function even shorter than this. For a one-line function, we can remove the {
and }
. Additionally, we can even remove the return
! This is because one-line arrow functions have an implicit return. This makes for really short functions, as you can see below.
let addFn3 = (x, y) => x + y;
These one-line functions are really useful when you’re using functions like forEach
and map
. They make for much more readable code. In the example below, you may notice one more interesting thing about arrow functions. When an arrow function only has one argument, you can omit the (
and )
. This really does make the function about as short as it can get!
let numbers = [1, 2, 3];
// Looping with a normal function
numbers.forEach(function(number) {
console.log(number);
});
// Looping with an arrow function
numbers.forEach(number => console.log(number));
Simple binding of this
Arrow functions are great for writing tidier code, but their real strength comes from how they bind this
. When an arrow function is created, its this
is set to that of the function that it is inside. To understand why this is useful, let’s take a quick look at how this
works.
What is this
?
There are two rules you need to know about this
for this example to make sense.
- Creating a new function creates a new
this
- A function in an object has
this
set to the object itself
In the code below, we have an API
object which processes IDs. The function processIds
takes an array, loops through it and calls this.processId
for each one. In the highlighted line below, when this.processId
is called, this
refers to the API
(as per rule 2 above), and everything works as expected.
let API = {
processIds: function(ids) {
for(let i=0; i<ids.length; i++) {
this.processId(ids[i]);
}
},
processId: function(id) {
console.log(`Processing ID: ${id}`);
}
};
API.processIds([1, 2, 3]); // Processing ID ...
Now, say that we want to get rid of our for
loop, and take a more “functional” approach, using the forEach
function. If you try running the code below, you will get an error - this.processId
doesn’t exist. Why? The call to this.processId
is now inside another function, which has its own this
(thanks to rule 1).
let API = {
processIds: function(ids) {
ids.forEach(function(id) {
this.processId(id);
});
},
processId: function(id) {
console.log(`Processing ID: ${id}`);
}
};
API.processIds([1, 2, 3]); // Uncaught TypeError: this.processId is not a function
ES5 introduced the ability to force a function’s this
to be something else. We could fix our problem above by calling .bind(this)
on the function we pass to forEach
. This will ensure that the function’s this
refers to API
and not itself.
processIds: function(ids) {
ids.forEach(function(id) {
this.processId(id);
}.bind(this));
}
No more this
surprises
Bind works absolutely fine, but in practice you will find that it can get tedious pretty quickly. If you are doing a lot of functional-style programming, your files will be cluttered with bind
. Arrow functions can solve this problem much more simply, and make your code more readable. As mentioned previously, arrow functions take their parent function’s this
.
let API = {
processIds: function(ids) {
ids.forEach(id => this.processId(id));
},
processId: function(id) {
console.log(`Processing ID: ${id}`);
}
};
API.processIds([1, 2, 3]); // Processing ID ...
If you start using arrow functions, you will save yourself a lot of nasty this
surprises in the future. This is one new feature you should start using right away!