DEV Community

Cover image for ES6 ⇒ Arrow Functions
skaytech
skaytech

Posted on • Edited on • Originally published at blog.skay.dev

ES6 ⇒ Arrow Functions

Introduction

In this article, we will look at one of the most popular features introduced with ES6, the arrow functions. Our minds are trained to learn a concept, and then when we want to apply the concept via code, we look up for the syntax.

But, what happens if there are multiple syntaxes that exist for a single function? That's precisely what you'll encounter when you first learn arrow functions. It's not so much with the concept, but the varying syntaxes that exist which throw the mind into a loop.

Let me introduce the various flavors of arrow functions with examples to ensure you can follow.

Arrow Function ⇒ The Basic flavor

If you would like to do a quick recap on JavaScript functions, you can read about it over here.

/* A simple function that returns a greeting */
const greeting1 = function() {
    console.log('Greeting1: Hi! How are you?');
}

/* An Arrow function that returns a greeting */
//In the syntax notice that the word 'function' is removed and after the brackets the '=>' symbol is added
const greeting2 = () => {
    console.log('Greeting2: Hi! How are you?');
}

greeting1(); //output -> Greeting1: Hi! How are you?
greeting2(); //Output -> Greeting2: Hi! How are you?
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • We removed the function keyword and immediately added an arrow after the angular brackets.

Arrow Function ⇒ No Params, Single line function

When the function has a single line statement within its body, you can further shorten it by removing the curly braces {} as shown below.

//Basic Arrow Function as shown in the above example
const greeting2 = () => {
    console.log('Greeting2: Hi! How are you?');
}

//Shorter syntax
const greeting3 = () => console.log('Greeting3: Hi! How are you?');

greeting2(); //Greeting2: Hi! How are you?
greeting3(); //Greeting3: Hi! How are you?
Enter fullscreen mode Exit fullscreen mode

Arrow Function ⇒ Single Param, Single line function

When you have only one parameter passed into the function, then you can remove the angular brackets around the param name as shown below.

//Basic Arrow Function as shown in the above example
const greeting2 = (name) => {
    console.log(`Greeting2: Hi ${name}, How are you?`);
}

//Shorter syntax
const greeting3 = name => console.log(`Greeting3: Hi ${name}, How are you?`);

greeting2('Skay'); //Greeting2: Hi Skay, How are you?
greeting3('Skay'); //Greeting3: Hi Skay, How are you?
Enter fullscreen mode Exit fullscreen mode

Arrow Function ⇒ Single Param, Multiple lines function

If the function contains multiple lines, then the curly braces are compulsory.

//Single Param with multiple lines in the function
const greeting2 = name => {
    const greeting = `Greeting2: Hi ${name}, How are you?`;
    console.log(greeting);
}

greeting2('Skay'); //Greeting2: Hi Skay, How are you?
Enter fullscreen mode Exit fullscreen mode

Arrow Function ⇒ Multiple Params, Single & Multiple lines function

Multiple Parameters/Single line function

When there are multiple parameters passed to a function, the angular brackets are compulsory as shown in below.

//Multiple Params with single lines
const greeting = (name, membershipType) => 
             console.log(`Greeting: Hi ${name}, Are you ${membershipType} member?`);

greeting('Skay', 'Special Edition'); //Greeting: Hi Skay, Are you a Special Edition member?
Enter fullscreen mode Exit fullscreen mode

Multiple Parameters/Multiple lines function

The angular brackets around the parameters are compulsory when there are multiple parameters. Likewise, the curly brackets are also compulsory when you have multiple lines.

//Multiple Params with multiple lines
const greeting = (name, membershipType) => {
    const memberGreeting = `Greeting: Hi ${name}, Are you ${membershipType} member?`;
    console.log(memberGreeting);
}

greeting('Skay', 'Special Edition'); //Greeting: Hi Skay, Are you a Special Edition member?
Enter fullscreen mode Exit fullscreen mode

So, far we've seen the various syntaxes under different combinations of parameters vs the statements inside the function body. While they are fancy, we still haven't seen the real use-cases where you'll acknowledge the true strength of arrow functions.

Implicit Return

Let us first look at a function returning a value and how we can use the arrow function's syntax to use its implicit return feature.

Non-Arrow Function:

//Function Expression returning the name of the fruit
const getFruitName = function() {
    return 'Mango';
}

//Display the name of the fruit on the console
console.log(getFruitName());
Enter fullscreen mode Exit fullscreen mode

Arrow Function:

//When we convert the above function into an Arrow function
const getFruitName = () => 'Mango';

console.log(getFruitName());
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • Notice that the 'return' statement is completely omitted and the string value 'Mango' is returned when the function getFruitName() is invoked. This is known as an implicit return.
  • This is very powerful since it significantly improves readability especially while chaining promise functions.

Let us look at a real example, where the code readability improves drastically when implicit return syntax is used.

Non-Arrow Function:

The below code fetches a list of Github users using the Fetch API. The comments within the code highlight the explanation flow.

/*
    Function getGithubUsers returns a list of 30 users by default
    The function returns a promise with the GitHub users array.
*/

function getGithubUsers() {

    //Using Fetch API make a call to the github's get Users API
    return fetch('https://api.github.com/users')
        .then(function(response) {
            //If the call to the API is successful, then it returns the response object
            //Returning the JSON object within the response object which contains the actual users data
            return response.json();
        }).then(function(data) {
            //The response.data() returned from the previous function is resolved into the data object
            //This data object is an array containing the gitHub users            
            return data;
        });
}

//Call the getGithubUsers() function 
//If the response is successful, then the data object is returned which contains the github users
getGithubUsers()
    .then(function(data) {
        console.log(data);
    });
Enter fullscreen mode Exit fullscreen mode

Arrow Function:

With the use of arrow functions and through its implicit return syntax, the code is much easier to write as well as read.

/*
     Function getGithubUsers returns a list of 30 users by default
     The function returns a promise with the GitHub users array.
 */

function getGithubUsers() {

    //Using Fetch API make a call to the github's get Users API
    return fetch('https://api.github.com/users')
        .then(response => response.json())
        .then(data => data);
}

//Call the getGithubUsers() function 
//If the response is successful, then the data object is returned which contains the github users
getGithubUsers()
    .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • We have removed the 'function' keyword & added the ⇒ symbol to make it into an arrow function.
  • We have then removed the 'angular' brackets since it has only one parameter, i.e., response.
  • We have then removed the 'return' keyword since arrow functions have an implicit return.

We've just combined all of the things you've learned thus far from the above examples. But, when they are combined, the code is much less and it's much more cleaner. It might be a little overwhelming at first, but you'll get used to it once you start using it.

Arrow Functions solve "this" keyword lookup

Context

Whenever a JavaScript code runs, it runs within the context either at a global scope (window) or function scope or block scope.

Within such a context, we can use 'this' keyword to reference the object. The reference of the object changes based on where you are using the 'this' keyword.

Let us look at the code sample below:

//Global Function 'bark' displays the value of 'this' on the console
function bark() {
    //In this case, 'this' refers to the global object which is the 'window'
    console.log(this); //Output -> Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
}

//An object declaration 'dog' 
const dog = {
    name: 'Pluto',
    breed: 'Doberman',
    bark: function() {
        //In this case, 'this' refers to the dog object
        console.log(this); //output -> {name: 'Pluto', breed: 'Doberman'}
        return "Woof Woof";
    }
}

//Invoke the bark and dog.bark functions
bark();
dog.bark();
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • When the function 'bark' is invoked, the console.log(this) statement outputs the window object. The reason is whenever a call is made to 'this' keyword, by default a lookup is done to the parent object of the function, and in this case, it's the window object.
  • When the function 'dog.bark()' is invoked, the console.log(this) statement outputs the dog object. Again, the reason is the lookup done from the bark function which is inside the 'dog' object and hence the reference to 'this' is the dog object itself.

I hope the above statements made sense.

This Keyword with an anonymous function

Now, let's look at another example of using the 'this' keyword with an anonymous function.

//Office Constructor that accepts employees as an object array
const Office = function(employees) {
    this.employees = employees;
    this.department = 'Marketing'
    this.greeting = function() {
        this.employees.forEach(function(employee) {
            console.log(`Hello ${employee.name}. Welcome to our ${this.department} department.`);
            //Output -> Hello John. Welcome to our undefined department.
            //Output -> Hello Sarah. Welcome to our undefined department.
                        //If you console.log(this) over here, it'll reference the window object.
        })
    }
}

//Creating an employees array with 2 employees, John & Sarah
const employees = [{
        name: 'John',
        experience: '10 yrs'
    },
    {
        name: 'Sarah',
        experience: '20 yrs'
    }
];

//Creating myOffice object using the constructor 'Office' and passing the 'employees' as a parameter
const myOffice = new Office(employees);

//Invoke the greeting() method of myOffice object created
myOffice.greeting();
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • const Office is a constructor that accepts employees as a parameter.
  • const myOffice is the object created by passing in the employees array consisting of John & Sarah.
  • When myOffice.greeting() method is invoked, it runs the forEach loop on this.employees array. Here the 'this' keyword refers to the 'myOffice' object.
  • An anonymous function is created within the forEach block and within the forEach block, when the 'this' keyword is referenced in the console.log statement for 'this.department', undefined is output.
  • So, what happened here? We know that based on previous examples that whenever a 'this' keyword is referenced, the JavaScript compiler references the parent object and should have referenced the 'myOffice' object.
  • However, with Anonymous functions, it creates a new scope and within the new scope, the parent object becomes the window object, and hence when 'this' keyword is referenced within the anonymous function, it references the window object.

I hope that made sense. And if we change the above example to arrow functions, it no longer creates a local scope and references the parent object 'myOffice' as it should have and this is what arrow function precisely addresses.

//Office Constructor that accepts employees as an object array
const Office = function(employees) {
    this.employees = employees;
    this.department = 'Marketing'
    this.greeting = function() {
        this.employees.forEach(employee => {
            console.log(`Hello ${employee.name}. Welcome to our ${this.department} department.`);
            //Output -> Hello John. Welcome to our Marketing department.
            //Output -> Hello Sarah. Welcome to our Marketing department.
        })
    }
}

//Creating an employees array with 2 employees, John & Sarah
const employees = [{
        name: 'John',
        experience: '10 yrs'
    },
    {
        name: 'Sarah',
        experience: '20 yrs'
    }
];

//Creating myOffice object using the constructor 'Office' and passing the 'employees' as a parameter
const myOffice = new Office(employees);

//Invoke the greeting() method of myOffice object created
myOffice.greeting();
Enter fullscreen mode Exit fullscreen mode

In the above example, we've changed the anonymous function into an arrow function by removing the function keyword and including the ⇒ symbol. It can further be shortened by removing the curly braces since there's only one statement within the function.

Conclusion

So to recap, arrow functions are one of the most important and powerful features introduced with ES6 and it primarily addresses two aspects:

  • Improves readability by having shorter syntax.
  • Arrow functions do not have their own 'this', instead, they look up to their parent scope to determine what the 'this' is referencing.

I hope you enjoyed this article. Do let me know your comments, feedback, and share it with your friends.

You may also like:

Top comments (0)