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.
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.
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 });
Optional parentheses in case of a single parameter
Must use parentheses in case of multiple parameters
return
keyword is not required for a single return expression in the function body
return
keyword required in case of multiple statements in the function
To return an object notation, wrap it with parentheses
Rest operator, default parameters, and destructuring of parameters work normally
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);
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)))
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");
})
});
this
keyword is arrow function is inherited from the scope in which it is definedAn 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
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,
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);
arguments
object not available for arrow functionsarguments
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
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