Arrow Functions

In this lesson, you will learn the new concise syntax for creating functions, which helps us with scoping.

One of the most popular new features in ECMAScript 2015 is arrow functions. Is it so popular because of the new cleaner syntax or for sharing this with the parent scope? Maybe both. Let’s look into them in more detail.

Syntax

First arrow functions were introduced to provide a shorter function syntax. The syntax can look a bit different depending on the function. Let’s see some options!

Here is a function written in ES5 syntax:

var sum = function(a, b) {
  return a + b;
}

sum(1, 2) // -> 3

And here is the same function as an arrow function:

const sum = (a, b) => a + b;

sum(1, 2) // -> 3

As you can see, the arrow function makes the code much more concise when we have one-line expressions. Due to implicit return, we can omit the curly braces and return the keyword.

If we have multiple lines in the function, we always have to use the curly braces and return:

const printSum = (a, b) => {
  const sum = a + b;
  console.log('Answer: ' + sum);
  
  return sum;
}

printSum(1, 2); // -> Answer: 3

Parentheses are optional when only one parameter is present:

const square = n => n * n;

square(3); // -> 9 

Arrow functions are sometimes called fat arrow functions because of the token => that resembles a fat arrow.

We commonly use arrow functions when using array methods. Here, we create a new array with the ages of the players by mapping through the player array:

const players = [
  { name:'Michael', age:44},
  { name:'Karl', age:33},
  { name:'Lisa', age:37}
];

const ages = players.map(player => player.age);  // [44, 33, 37]

Array functions really shine here!


This

You have seen how nice the new syntax can be. However, there is another better reason to use arrow functions, and that is the sometimes-confusing behavior of the this keyword in JavaScript. Arrow function doesn’t depend on where it’s declared but on how and where the function is called, its execution context.

In the global scope, the value of this is always the window object.

Regular functions

Functions have their own execution context and with it comes their own this. Understanding these concepts is not easy, but it is important.

Hopefully, some example code can make it clearer. To show the difference in execution context, we use setTimeout, a native JavaScript function, which calls a function after a set delay in milliseconds.

// ES5
function Age() {
  this.age = 42;

  setTimeout(function() {
    console.log(this.age); // -> undefined
  }, 1000);
}

var a = new Age();

If we run this code, we get undefined logged to the console because the function inside setTimeout has its own execution context. So, we are not accessing this.age of the Age function but of the one inside the function where we make the this.age call, which in this case is setTimeout. And setTimeout hasn’t assigned a value to age.

To access the context of the parent function, developers often reassign the this in the outer function to a variable commonly named that or self.

// ES5
function Age() {
  var that = this;
  that.age = 42;

  setTimeout(function() {
    console.log(that.age); // -> 42
  }, 1000);
}

var a = new Age();

This way, we can access the this of the parent function. However, you can see how it can be confusing.

Another option is to use function {}.bind(this).

Arrow functions

Arrow functions, however, share the lexical scope with their parent. This means that they use this from the code that contains the arrow function.

// ES6
function Age() {
  this.age = 42;

  setTimeout(() => {
    console.log(this.age); // -> 42
  }, 1000);
}

const a = new Age();

So, you can see how arrow functions help to make the code cleaner and easier to read.

Should we always use arrow functions?

Arrow functions are great, but regular functions are still useful. Let’s look at some examples of when we should not use arrow functions.

Methods

Arrow functions are best suited for non-method functions. Let’s see what happens when we try to use them as methods. In this example, we create an object called blog with a method like.

const blog = {
  likes: 0,
  like: () => {
    this.likes++;
  }
}

One would think that every time we call blog.like() the attribute blog.likes increases by one. However, the value of likes will sadly remain at zero.

This time, the parent scope is the window object and not the blog object. Therefore, invoking the like() method would attempt to increment the property likes on the window object.

If we use the traditional syntax instead, it will work as expected:

const blog = {
  likes: 0,
  like: function() {
    this.likes++;
  }
}

Even better, another new feature in ES6 is shorthand method syntax:

const blog = {
  likes: 0,
  like() {
    this.likes++;
  }
}

Constructor

An arrow function cannot be used as a constructor. It will throw an error when used with new:

const PrintDouble = (x) => console.log(2 * x);

new PrintDouble(2);

// -> TypeError: PrintDouble is not a constructor

We can use a function expression instead:

const PrintDouble = function(x) {
  console.log(2 * x);
};

new PrintDouble (2); // -> 4

Conclusion

Arrow functions are a great new addition in ES6 that brings us a syntactically compact alternative to a regular function. An arrow function does not have its own this. The this value of the enclosing lexical scope is used. Arrow functions are ill-suited as methods, and they cannot be used as constructors.


Quiz on arrow functions

Get hands-on with 1200+ tech skills courses.