Javascript Arrow functions

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.

  1. Creating a new function creates a new this
  2. 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!

Other recent posts: