What are the benefits of arrow functions in JavaScript?

  • They are more concise.
  • They have implicit returns. (We’ll get into this below.)
  • They do not rebind the value of this when you use an arrow function inside another function. (We’ll get into this in a later post.)

How do I convert my ES5 functions to ES6 arrow functions?

Let’s start with a simple example using .map().

// Let's define an array of first names:
const names = ['joe', 'rache', 'micaela'];
// If I want to add my last name to each I'll run the following function using .map():
const fullNames = names.map(function(name) {
return `${name} cardillo`;
});
// In the console when I call:
names
// It returns:
['joe', 'rache', 'micaela']
// Then if I call the function:
fullNames
// It returns:
["joe cardillo", "rache cardillo", "micaela cardillo"]

To start, delete the word ‘function’ and replace it with a fat arrow =>

const fullNames = names.map((name) => {
return `${name} cardillo`;
});

If you have only one parameter you can remove the parenthesis from it:

const fullNames = names.map(name => {
return `${name} cardillo`;
});

You can also do an “implicit return.”

But first, what is an explicit return?

An explicit return is when you explicitly write the word return in the function.

To do an implicit return, remove the word return, then move what you are returning up to the same line as the rest of the function. You can remove the curly brackets.

// Explicit return:
const fullNames = names.map(name => {
return `${name} cardillo`;
});
// Implicit return:
const fullNames = names.map(name => `${name} cardillo`);

If there is no parameter you can replace it with parenthesis:

const fullNames = names.map(() => `hey cardillo!`);
// Returns:
["hey cardillo!", "hey cardillo!", "hey cardillo!"]

Arrow functions are always anonymous functions.

Before describing an anonymous function, let’s first answer the question, “What is a named function?”

// In ES5 a named function can be written as follows:
function myFavFood(food) {
console.log(`My favorite food is ${food}!`);
}
// myFavFood is the name of the function.
// Calling this in the console:
myFavFood('pizza');
// Returns:
My favorite food is pizza!

In the above arrow functions, notice that we set all of them equal to a constvariable called fullNames.

So why would we want to set our arrow function equal to a variable?

One reason is that it can help us debug JavaScript errors, since sometimes the line number isn’t enough to narrow down where it’s coming from.

Using the food example, we can assign the arrow function to a const variable called myFavFood.

const myFavFood = (food) => { console.log(`My favorite food is ${food}!`) }
// In the console:
myFavFood('pizza');
// Returns:
My favorite food is pizza!

An introductory guide to ES6 arrow functions in JavaScript

No comments yet

What are the benefits of arrow functions in JavaScript?

  • They are more concise.
  • They have implicit returns. (We’ll get into this below.)
  • They do not rebind the value of this when you use an arrow function inside another function. (We’ll get into this in a later post.)

How do I convert my ES5 functions to ES6 arrow functions?

Let’s start with a simple example using .map().

// Let's define an array of first names:
const names = ['joe', 'rache', 'micaela'];
// If I want to add my last name to each I'll run the following function using .map():
const fullNames = names.map(function(name) {
return `${name} cardillo`;
});
// In the console when I call:
names
// It returns:
['joe', 'rache', 'micaela']
// Then if I call the function:
fullNames
// It returns:
["joe cardillo", "rache cardillo", "micaela cardillo"]

To start, delete the word ‘function’ and replace it with a fat arrow =>

const fullNames = names.map((name) => {
return `${name} cardillo`;
});

If you have only one parameter you can remove the parenthesis from it:

const fullNames = names.map(name => {
return `${name} cardillo`;
});

You can also do an “implicit return.”

But first, what is an explicit return?

An explicit return is when you explicitly write the word return in the function.

To do an implicit return, remove the word return, then move what you are returning up to the same line as the rest of the function. You can remove the curly brackets.

// Explicit return:
const fullNames = names.map(name => {
return `${name} cardillo`;
});
// Implicit return:
const fullNames = names.map(name => `${name} cardillo`);

If there is no parameter you can replace it with parenthesis:

const fullNames = names.map(() => `hey cardillo!`);
// Returns:
["hey cardillo!", "hey cardillo!", "hey cardillo!"]

Arrow functions are always anonymous functions.

Before describing an anonymous function, let’s first answer the question, “What is a named function?”

// In ES5 a named function can be written as follows:
function myFavFood(food) {
console.log(`My favorite food is ${food}!`);
}
// myFavFood is the name of the function.
// Calling this in the console:
myFavFood('pizza');
// Returns:
My favorite food is pizza!

In the above arrow functions, notice that we set all of them equal to a constvariable called fullNames.

So why would we want to set our arrow function equal to a variable?

One reason is that it can help us debug JavaScript errors, since sometimes the line number isn’t enough to narrow down where it’s coming from.

Using the food example, we can assign the arrow function to a const variable called myFavFood.

const myFavFood = (food) => { console.log(`My favorite food is ${food}!`) }
// In the console:
myFavFood('pizza');
// Returns:
My favorite food is pizza!

Arrow functions were introduced with ES6 as a new syntax for writing JavaScript functions. They save developers time and simplify function scope. They are undoubtedly one of the more popular features of ES6.

Here is a function is written in ES5 syntax:

function timesTwo (params) {
  return params * 2
}
timesTwo(4);  // 8

Now, here is the same function expressed as an arrow function:

var timesTwo = params => params * 2

timesTwo(4); // 8

It’s much shorter! We are able to omit the curly braces and the return statement due to implicit returns (but only if there is no block — more on this below).

It is important to understand how the arrow function behaves differently compared to the regular ES5 functions.

One thing you will quickly notice is the variety of syntaxes available in arrow functions. Let’s run through some of the common ones:

1. No parameters

If there are no parameters, you can place an empty parentheses before =>.

() => 42

In fact, you don’t even need the parentheses!

_ => 42

2. Single parameter

With these functions, parentheses are optional:

x => 42  || (x) => 42

3. Multiple parameters

Parentheses are required for these functions:

(x, y) => 42

4. Statements (as opposed to expressions)

In its most basic form, a function expression produces a value, while a function statement performs an action.

With the arrow function, it is important to remember that statements need to have curly braces. Once the curly braces are present, you always need to writereturn as well.

Here is an example of the arrow function used with an if statement:

var feedTheCat = (cat) => {
  if (cat === 'hungry') {
    return 'Feed the cat';
  } else {
    return 'Do not feed the cat';
  }
}

5. “Block body”

If your function is in a block, you must also use the explicit return statement:

var addValues = (x, y) => {
  return x + y
}

6. Object Literals

If you are returning an object literal, it needs to be wrapped in parentheses. This forces the interpreter to evaluate what is inside the parentheses, and the object literal is returned.

x =>({ y: x })

Syntactically anonymous

It is important to note that arrow functions are anonymous, which means that they are not named.

This anonymity creates some issues:

  1. Harder to debug

When you get an error, you will not be able to trace the name of the function or the exact line number where it occurred.

2. No self-referencing

If your function needs to have a self-reference at any point (e.g. recursion, event handler that needs to unbind), it will not work.

In classic function expressions, the this keyword is bound to different values based on the context in which it is called. With arrow functions however, this is lexically bound. It means that it usesthis from the code that contains the arrow function.

For example, look at the setTimeout function below:

// ES5
var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(function() {
      console.log(this.id);
    }.bind(this), 1000);
  }
};

In the ES5 example, .bind(this) is required to help pass the this context into the function. Otherwise, by default this would be undefined.

// ES6
var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(() => {
      console.log(this.id);
    }, 1000);
  }
};

ES6 arrow functions can’t be bound to a this keyword, so it will lexically go up a scope, and use the value of this in the scope in which it was defined.

When you should not use Arrow Functions

After learning a little more about arrow functions, I hope you understand that they do not replace regular functions.

Here are some instances where you probably wouldn’t want to use them:

  1. Object methods

When you call cat.jumps, the number of lives does not decrease. It is because this is not bound to anything, and will inherit the value of this from its parent scope.

var cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

2. Callback functions with dynamic context

If you need your context to be dynamic, arrow functions are not the right choice. Take a look at this event handler below:

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

If we click the button, we would get a TypeError. It is because this is not bound to the button, but instead bound to its parent scope.

3. When it makes your code less readable

It is worth taking into consideration the variety of syntax we covered earlier. With regular functions, people know what to expect. With arrow functions, it may be hard to decipher what you are looking at straightaway.

When you should use them

Arrow functions shine best with anything that requires this to be bound to the context, and not the function itself.

Despite the fact that they are anonymous, I also like using them with methods such as map and reduce, because I think it makes my code more readable. To me, the pros outweigh the cons.

Proper ways of using ES6 arrow functions in JavaScript

No comments yet

Arrow functions were introduced with ES6 as a new syntax for writing JavaScript functions. They save developers time and simplify function scope. They are undoubtedly one of the more popular features of ES6.

Here is a function is written in ES5 syntax:

function timesTwo (params) {
  return params * 2
}
timesTwo(4);  // 8

Now, here is the same function expressed as an arrow function:

var timesTwo = params => params * 2

timesTwo(4); // 8

It’s much shorter! We are able to omit the curly braces and the return statement due to implicit returns (but only if there is no block — more on this below).

It is important to understand how the arrow function behaves differently compared to the regular ES5 functions.

One thing you will quickly notice is the variety of syntaxes available in arrow functions. Let’s run through some of the common ones:

1. No parameters

If there are no parameters, you can place an empty parentheses before =>.

() => 42

In fact, you don’t even need the parentheses!

_ => 42

2. Single parameter

With these functions, parentheses are optional:

x => 42  || (x) => 42

3. Multiple parameters

Parentheses are required for these functions:

(x, y) => 42

4. Statements (as opposed to expressions)

In its most basic form, a function expression produces a value, while a function statement performs an action.

With the arrow function, it is important to remember that statements need to have curly braces. Once the curly braces are present, you always need to writereturn as well.

Here is an example of the arrow function used with an if statement:

var feedTheCat = (cat) => {
  if (cat === 'hungry') {
    return 'Feed the cat';
  } else {
    return 'Do not feed the cat';
  }
}

5. “Block body”

If your function is in a block, you must also use the explicit return statement:

var addValues = (x, y) => {
  return x + y
}

6. Object Literals

If you are returning an object literal, it needs to be wrapped in parentheses. This forces the interpreter to evaluate what is inside the parentheses, and the object literal is returned.

x =>({ y: x })

Syntactically anonymous

It is important to note that arrow functions are anonymous, which means that they are not named.

This anonymity creates some issues:

  1. Harder to debug

When you get an error, you will not be able to trace the name of the function or the exact line number where it occurred.

2. No self-referencing

If your function needs to have a self-reference at any point (e.g. recursion, event handler that needs to unbind), it will not work.

In classic function expressions, the this keyword is bound to different values based on the context in which it is called. With arrow functions however, this is lexically bound. It means that it usesthis from the code that contains the arrow function.

For example, look at the setTimeout function below:

// ES5
var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(function() {
      console.log(this.id);
    }.bind(this), 1000);
  }
};

In the ES5 example, .bind(this) is required to help pass the this context into the function. Otherwise, by default this would be undefined.

// ES6
var obj = {
  id: 42,
  counter: function counter() {
    setTimeout(() => {
      console.log(this.id);
    }, 1000);
  }
};

ES6 arrow functions can’t be bound to a this keyword, so it will lexically go up a scope, and use the value of this in the scope in which it was defined.

When you should not use Arrow Functions

After learning a little more about arrow functions, I hope you understand that they do not replace regular functions.

Here are some instances where you probably wouldn’t want to use them:

  1. Object methods

When you call cat.jumps, the number of lives does not decrease. It is because this is not bound to anything, and will inherit the value of this from its parent scope.

var cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

2. Callback functions with dynamic context

If you need your context to be dynamic, arrow functions are not the right choice. Take a look at this event handler below:

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

If we click the button, we would get a TypeError. It is because this is not bound to the button, but instead bound to its parent scope.

3. When it makes your code less readable

It is worth taking into consideration the variety of syntax we covered earlier. With regular functions, people know what to expect. With arrow functions, it may be hard to decipher what you are looking at straightaway.

When you should use them

Arrow functions shine best with anything that requires this to be bound to the context, and not the function itself.

Despite the fact that they are anonymous, I also like using them with methods such as map and reduce, because I think it makes my code more readable. To me, the pros outweigh the cons.