JavaScript Arrow Functions

JavaScript Arrow Functions

Syntax; How to use; and Gotchas

Featured on daily.dev

Hello folks! In this article, we'll learn about Arrow Functions in JavaScript – what are they, how to use them, and their gotchas. If you're a beginner, I hope this helps you understand the concept well. And if you're already familiar with the concept, you might want to go through its gotchas and when not to use them.

What are Arrow Functions?

Arrow functions were introduced with ES6 as a new way to write functions in JavaScript. The newer syntax made the code less verbose and more readable, as we don't need to write function and return keywords.

They are different from traditional functions in 2 aspects – syntax and scoping. The difference in syntax is clear and easy to pick, but the difference in scoping is rather subtle and it's important to understand the difference as it can break or alter the behaviour of the code.

Syntax

Arrow functions can be written in different ways depending upon the number of parameters it accepts and the operation it performs.

// 1. One parameter, and a single return statement
const square = x => x*x;

// 2. Multiple parameter, and a single return expression
const sum = (x, y) => x + y;

// 3. Multiple statements in function expression
const sum = (x, y) =>{
    console.log(`Adding ${x} and ${y}`);
    return x + y;
}

// 4. Returning an object
const sumAndDifference = (x, y) => ({ sum: x + y, difference: x - y });

Quick Cheatsheet

  1. Optional parentheses in case of a single parameter

  2. Must use parentheses in case of multiple parameters

  3. return keyword is not required for a single return expression in the function body

  4. return keyword required in case of multiple statements in the function

  5. To return an object notation, wrap it with parentheses

  6. Rest operator, default parameters, and destructuring of parameters work normally

When to use Arrow Functions?

For passing anonymous callback functions to array methods

The array methods like .map(), .filter(), .reduce(), etc. can look really concise and clean when arrow functions are used to pass the callback functions.

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((a, b) => a + b, 0);

Promise Chaining

The promise chaining can look very confusing with repetitive function and return keywords. By using arrow functions, we can write promise chains with minimal code and a more readable manner.

fetch(URL)
.then(res => res.json())
.then(json => json.data)
.then(data => data.map(dataItem => console.log(dataItem)))

Callback Functions

In JavaScript, we write a lot of callback functions, and using arrow functions to do that can make it really clean

setTimeout(() => {
    console.log("I'll get logged first");
    setTimeout(() => {
        console.log("I'll get logged later");
    })
});

Gotchas

this keyword is arrow function is inherited from the scope in which it is defined

This restricts the use of arrow functions as object methods.

An object method defined using traditional function creates its own scope and this refers to the object which has the method stored as its property.

Since arrow functions use lexical scoping for this, if an object method is defined using arrow functions, this would not refer to the object which has this method stored as a property, instead, it would refer to its parent scope.

Example:

const obj = {
    name: "Hetav Desai",
    printWithTraditionalFunction: function () {
        console.log(this.name);
    }
    printWithArrowFunction: () => console.log(this.name)
}

obj.printWithTraditionalFunction();
// Hetav Desai

obj.printWithArrowFunction();
// undefined

Callback functions and this

You should also be careful while using arrow functions for defining callbacks that require this. Example:

// Callback using arrow function
function counter1() {
  this.count = 0;
  setInterval(() => {
    this.count++
    console.log(this.count);
  }, 1000)
}
counter1();

// Callback using traditional function
function counter2() {
  this.count = 0;
  setInterval(function() {
    this.count++
    console.log(this.count);
  }, 1000)
}
counter2();

In the above example, this keyword in the first function refers to the function counter1 and hence variable count can be accessed inside callback function using this.count, and the function increments a number every second and prints it.

While in the case of function counter2, this keyword inside callback function refers to its own scope and not that of counter2. Hence, it cannot access count variable and prints NaN every second,

Unlike traditional functions, arrow functions are not hoisted

Because of this, you cannot call them before they are declared.

// Using Normal Function
printWithTraditionalFunction("Hetav") // prints Hetav
function printWithTraditionalFunction(name) {
    console.log(name)
}

// Using Arrow Function
printWithTraditionalFunction("Hetav") // throws ReferenceError
const printWithArrowFunction = name => console.log(name);

Binding arguments object not available for arrow functions

arguments is an Array-like object accessible inside functions that contain the values of the arguments passed to that function. But it is not available for arrow functions.

function something() {
    console.log(arguments.length); // prints the number of arguments passed
}
something(1, 2, 3) // prints 3

const something = () => {
    console.log(arguments.length); // prints the number of arguments passed
}
something(1, 2, 3) // throws Reference Error: arguments not found

Now that we cannot access arguments in arrow functions, does it mean that we are forced to use traditional functions? Nope, not at all.

Rest parameters of ES6 to the rescue

const something = (..args) => {
    console.log(args.length); // prints the number of arguments passed
}
something(1, 2, 3) // prints 3

Conclusion

In this article, we saw that JavaScript arrow functions are really awesome and have minimal and readable syntax which makes it very easy for developers to write clean code. However, there are certain scenarios where using traditional functions is recommended. Except that, I'd strongly suggest using arrow functions everywhere.

Thank you for reading the blog. Do drop your feedback in the comments below and if you liked it, share it with your developer friends who might find it useful too. If you want to have any discussion around this topic, feel free to reach out to me on Twitter

Did you find this article valuable?

Support Hetav Desai by becoming a sponsor. Any amount is appreciated!