ES6 Tutorial: A Complete Introduction to Arrow Functions in JavaScript8 min read

JavaScript ES6 or ECMAScript 2015 is actually the biggest update made to ECMAScript since the first edition released in 1997. There are a number of features have been released in this major addition including the introduction of new keywords to declare variables, creating classes, promises for asynchronous actions, templating strings and many more.

In this article, we are going to explore one of the outstanding features in the ES6 additions, the new way to write functions syntactically compact and concise, which is the arrow function.

Getting Started

Before JavaScript ES6, there are a handful of ways to declare a function, which can be a function declaration, like this:

function add(a, b) {
    return a + b;
}
console.log(add(2, 3));

Or with function expression:

var add = function(a, b) {
    return a + b;
}
console.log(add(2, 3));

Arrow Functions Syntax

For now, let’s translate both two functions above into arrow functions, which might look like this:

var add = (a, b) => {
    return a + b;
}
console.log(add(2, 3)); // 5

We completely get rid of the function keyword, and instead we use the => syntax, which looks like an arrow, so people call it “arrow function” or “fat arrow function” (no idea why it’s fat).

Let’s break down the syntax of this arrow function. First, arrow functions need to be a function expression, you cannot just put it alone like a regular function. Parameters are passed inside the parentheses like a normal function. Next, the => indicates this is an arrow function and followed by the body of the function. We call the arrow function just like a regular function. Here is the general syntax:

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression
// equivalent to: => { return expression; }

// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }

// The parameter list for a function with no parameters should be written with a pair of parentheses.
() => { statements }

Arrow functions with parameters:

var myFunction = (para1, para2) => {
    // do something
}

If your function just has a single expression, you can omit the curly brackets, the return keyword and write a whole function on a single line:

var myFunction = (para1, para2) => para1 + para2;

When doing this, the arrow function allows you to have an implicit return, which means the expression will be returned when calling the function without the return keyword.

var myFunction = (para1, para2) => para1 + para2;
myFunction(2, 3); // 5

var greeting = name => "Hello, my name is " + name;
greeting('Nam'); // Hello, my name is Nam

Those functions are equal to:

var myFunction = (para1, para2) => { return para1 + para2; }
myFunction(2, 3); // 5
var greeting = name => { return "Hello, my name is " + name; }
greeting('Nam'); // Hello, my name is Nam

If you write a block of code inside the brackets to contain your function’s body, even if it is just a single statement, you still have to explicitly use the return keyword if you want to return something.

In case you want to implicitly return an object, make sure wrapping it inside parenthesis to avoid confusion:

var test = () => { name: 'Jessica' };
test(); // undefined

// OK
var test = () => ({ name: 'Jessica' })
test(); // { name: 'Jessica' }

When the function has only one parameter, the parentheses surrounding this is optional:

// OK
var square = num => num * num;
// OK, also
var square = (num) => num * num;

If the function has two or more than two parameters, the parentheses are mandatorily required, the same rule applies with a function has no parameter:

var a = (para1, para2) => {
    // do something
}

var b = (para1, para2, para3) => {
    // do something
}

var c = () => {
    // do something
}

Some advanced syntaxes with rest and default parameters in ES6 are also supported in arrow functions:

(param1, param2, ...rest) => { statements }

var multiply = (a, b, ...c) => {
    var accumulator = a * b;
    for (var i of c) {
        accumulator *= i;
    }
    return accumulator;
};
multiply(1, 2, 3, 4, 5); // 120

(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { 
statements }

var x = (para1 = 'JavaScript') => {
    return "I want to learn " + para1;
}
x(); // I want to learn JavaScript
x('HTML'); // I want to learn HTML

We will learn more about spread operator, rest and default parameters in upcoming articles.

Arrow functions and the “this” keyword

The “this” keyword might be one of the most elusive parts in JavaScript. I already wrote a comprehensive article about the “this” keyword within the arrow function. Check out this article to learn more about the “this” keyword.

In general, with regular functions, we always have to keep track of the “this” context and remember to bind it correctly, the problem would be cumbersome when we have multiple nested functions. With arrow functions, it’s much easier to work with the “this” keyword because arrow functions don’t have their own bindings to the “this” and if “this” is accessed inside the arrow function, it is taken from the outside.

var person = {
    name: 'Zeep',
    tasks: ['sweep the floor', 'clean the room', 'wash the dishes', 'take out the garbage'],
    doing: function() {
        this.tasks.forEach(task => {
            console.log(this.name + " needs to " + task);
        });
    }
}
person.doing();
/*
Zeep needs to sweep the floor
Zeep needs to clean the room
Zeep needs to wash the dishes
Zeep needs to take out the garbage
*/

In the code segment above, we see there is this.name inside the console, due to the arrow function doesn’t have its own binding to this, it will go outside and find, here it finds the doing() method, which belongs to the person object, then this in this case, will refer to the person object. Otherwise with regular function, this inside a function always refers to a global object, which is the window object. Hence, if we change the arrow function with a regular function, we get a different outcome:

var person = {
    name: 'Zeep',
    tasks: ['sweep the floor', 'clean the room', 'wash the dishes', 'take out the garbage'],
    doing: function() {
        this.tasks.forEach(function(task) {
// this now refers to the window object, and this.name is not specified, hence 'undefined' will be returned.
            console.log(this.name + " needs to " + task);
        });
    }
}
person.doing();
/* 
undefined needs to sweep the floor
undefined needs to clean the room
undefined needs to wash the dishes
undefined needs to take out the garbage
*/

Perhaps it’s beneficial when using arrow function as just a function or non-object method, but in term of creating methods for objects, arrow functions are not suitable:

var book = {
    title: 'Thinking, Fast and Slow',
    author: 'Daniel Kahneman',
    display: () => {
        return this.title + " is written by " + this.author;
    }
}
book.display();
// undefined is written by undefined

Arrow functions cannot be used as a constructor. They cannot be called with the new keyword.

var Book = (title, author) => {
    this.title = title;
    this.author = author;
}

var instance = new Book('To Kill a Mockingbird', 'Harper Lee'); // TypeError

When working with events, the this value inside a function should be dynamic, hence the arrow functions are not ideal in this case.

var element = document.querySelector('.elem');
element.addEventListener('click', () => {
    // this === Window
})
element.addEventListener('click', function() {
    // this === element
})

Arrow Functions have no “arguments”

In JavaScript, arguments is an Array-like object accessible inside functions that contains the values of the arguments passed to that function. Arrow functions don’t have their own bindings of “this” and also they don’t have arguments object. arguments are inherited from their parent function like “this”.

// OK with regular function
var temp = function() {
    var arr = Array.from(arguments);
    return arr;
}
temp(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

// Not so OK with arrow function

var temp = () => {
    var arr = Array.from(arguments);
    return arr;
}
temp(1, 2, 3, 4, 5); // Error

Arrow Functions In Array Manipulation

Now we have learned the basic syntax of the arrow function and saw some of its use cases. But for the most part, arrow functions are usually used for array manipulation with some methods such as map(), filter(), reduce().

For example, we have an array of objects and we will use arrow function and some methods listed above to manipulate the array.

var fruits = [
    {
        name: 'pear',
        price: 20
    },
    {
        name: 'grape',
        price: 15
    },
    {
        name: 'apple',
        price: 25
    }
];

Now let’s get the names of the fruits:

fruits.map(fruit => fruit.name); 
/*
pear
grape
apple
*/

Checking fruits have the price greater than or equal 20:

fruits.filter(fruit => fruit.price >= 20);
// [ { name: 'pear', price: 20 }, { name: 'apple', price: 25 } ]

Summing up the price of the fruits altogether:

fruits.reduce((accumulator, fruit) => accumulator + fruit.price, 0); // 60

As we can see, using arrow functions in those case make the code looks pretty clear and concise.

Also read:

Arrow Functions with Callback and Promise

When working with asynchronous or promises, with traditional ES5 the code usually contains a number of functions and return keywords. Hence, asynchronous code is also a great place to use arrow functions to make the code more concise and readable. Here is a simple example of chaining promises in ES5:

aAsync()
    .then(function () { return bAsync(); })
    .then(function () { return cAsync(); })
    .done(function () { finish(); });

With ES6, this piece of code above can be translated into a much shorter and succinct version:

aAsync().then(() => bAsync()).then(() => cAsync()).done(() => finish);

When you should not use Arrow Functions

Arrow functions generally are quite great, but we still cannot entirely replace regular functions with arrow functions. As aforementioned, just summing up, there are some situations you should not use arrow functions:

  • Object Methods
  • DOM Events
  • Prototype Methods
  • When you need to use an argument Object

Use arrow functions for all other occasions if you prefer.

Wrapping up

As a whole, the arrow function is one of the most favorable features of ES6, with bits of help of arrow functions, your code can look more neat and concise. This type of function is usually used in array manipulation, asynchronously code and many other scenarios. It cannot replace the regular function in some situations. It doesn’t have this, argument, cannot work as a constructor and calling it with a new keyword an error will occur.

Previous Article
Next Article

Sign up for newsletter

* indicates required

Categories

Archives