JavaScript Arrow Function [In-Depth Tutorial]

JavaScript Arrow Function [In-Depth Tutorial]

Introduction to JS Arrow Function

Arrow functions, also known as “fat arrow” functions, are a new feature in JavaScript that provides a concise and more expressive syntax for defining functions. They were introduced in ECMAScript 6 (also known as ECMAScript 2015) and have become widely used in modern JavaScript code.

They have become a popular feature of the language, thanks to their concise syntax and ability to capture the lexical this value. Arrow functions are often used in place of traditional function expressions and can make your code more readable and easier to write.

Instead of thefunctionkeyword, it uses an arrow (=>) made up of an equal sign and a greater-than character (not to be confused with the greater-than-or-equal operator, which is written>=).

const power = (base, exponent) => {
  let result = 1;
  for (let count = 0; count < exponent; count++) {
    result *= base;
  }
  return result;
};

Thearrowcomesafterthe list of parameters and is followed by the function’s body. It expresses something like “this input (the parameters) produces this result (the body).”

In this article, we will explore the syntax and usage of JavaScript arrow functions, including how to use them as methods, constructors, and in combination with other functions.


Using arrow functions

Functions allow us to reduce repetition, and different approaches including the arrow function style make it easy to achieve this. As stated, arrow functions allow us to be concise in a simple manner. Let’s start with some examples to show how we can use arrow functions in JavaScript.

Taking a single argument

Here is an example of an arrow function that takes a single argument x and returns its square:

let square = x => x * x;

console.log(square(3));

Output

9

As you can see, the arrow function has a shorter syntax than a regular function. It does not have a function keyword, a pair of curly braces, or a return statement. Instead, it consists of the => operator (which is sometimes referred to as the “fat arrow”), followed by the function body.

Taking multiple arguments

If the function takes more than one argument, you need to enclose the arguments in parentheses. Let’s illustrate this by creating an arrow function that adds two numbers parsed to it.

let add = (x, y) => x + y;

console.log(add(3, 4));

Output

7

Structuring arrow functions

Depending on the structure of the code block, arrow functions can be shaped in different ways e.g. no curly braces, etc.

If the function body is a single expression, you can omit the curly braces and the return statement. The value of the expression will be returned automatically. Let’s show this by creating an arrow function that squares a number.

let square = (x) => x * x;

console.log(square(3));

Output

9

The above code is equivalent to let square = x => { return x * x; }.

If the function body consists of multiple statements, you need to enclose them in curly braces and use a return statement to specify the value to be returned.

To showcase this scenario, we will create an arrow function that adds and multiplies its argument and returns an array.

let addAndMultiply = (x, y) => {
    let sum = x + y;
    let product = x * y;
    return [sum, product];
};

console.log(addAndMultiply(3, 4));

Output

[ 7, 12 ]

Lexical this inarrowfunctions

An important aspect of arrow functions is that they behave differently from normal functions. The difference is subtle but important. Arrow functions do not have their own value of this. The value of this in an arrow function is inherited from the enclosing (lexical) scope.

Functions have a special variablethisthat refers to the object via which the method was invoked. As the value ofthisis dynamically given based on the function invocation, it is sometimes called dynamicthis. A function is executed in two scopes-lexical and dynamic. A lexical scope is a scope that surrounds the function scope, and the dynamic scope is the scope that called the function (usually an object).

Consider this example:

    var greeter = { 
      default: "Hello ", 
      greet: function (names){ 
        names.forEach(function(name) { 
    console.log(this.default + name); //Cannot read property 
      'default' of undefined 
       }) 
      } 
    }     
    console.log(greeter.greet(['hello', 'world'])) 

We are passing a subroutine to theforEach()function on thenamesarray. This subroutine has an undefined value ofthis, and unfortunately, it does not have access tothisof the outer methodgreet. Clearly, this subroutine needs a lexicalthis,derivethisfrom the surrounding scope of thegreetmethod. Traditionally, to fix this limitation, we assign the lexicalthisinto a variable, which is then accessible to the subroutine via closure.

We can fix the earlier example as follows:

    var greeter = { 
      default: "Hello ", 
      greet: function (names){ 
        let that = this 
        names.forEach(function(name) { 
          console.log(that.default + name);  
       }) 
      } 
    }     
    console.log(greeter.greet(['hello', 'world']))  

This is a reasonable hack to simulate lexicalthis. However, the problem with such hacks is that it creates too much noise for the person writing or reviewingthiscode. First, you have to understand the quirk of the behavior ofthis. Even if you understandthisbehavior well, you will need to continuously remain on the lookout for such hacks in your code.

Arrowfunctions have lexicalthisand do not require such a hack. They are more suited as subroutines because ofthis. We can covert the preceding example to use lexicalthisusing thearrowfunction:

    var greeter = { 
      default: "Hello ", 
      greet: function (names){ 
        names.forEach(name=> { 
          console.log(this.default + name);   //lexical 'this' 
           available for this subroutine 
       }) 
     } 
    }     
    console.log(greeter.greet(['hello', 'world']))  

Benefits of arrow function

Arrow functions have a number of benefits and features that make them useful in various situations. Here are a few examples:

  • Arrow functions do not have their own this value. They inherit the this value of the surrounding scope. This makes them particularly useful in object-oriented programming, where this often refers to the object that the function belongs to.
  • Arrow functions do not have a arguments object. If you need to access the arguments passed to a function, you can use the rest operator (...) instead.
  • Arrow functions can be used as callbacks and passed as arguments to other functions. For example, you can use them with array methods such as map(), filter(), and reduce().

Here is an example that uses an arrow function as a callback in the map() method

let numbers = [1, 2, 3, 4, 5];
let squares = numbers.map((x) => x * x);

console.log(squares); // [1, 4, 9, 16, 25]

Output

[ 1, 4, 9, 16, 25 ]

This approach makes it a lot easier to read.


Summary

In conclusion, arrow functions are a concise and powerful feature of JavaScript that allow developers to write shorter and more expressive code. They are particularly useful when working with higher-order functions and in scenarios where the this keyword might be confusing. However, it’s important to keep in mind that arrow functions behave differently than traditional functions in terms of their lexical this binding and cannot be used as constructors. Overall, arrow functions are a useful tool in any JavaScript developer’s toolkit and can greatly improve the readability and maintainability of your code.


References

Arrow function expressions - JavaScript | MDN (mozilla.org)

Olorunfemi Akinlua

Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital experiences across various domains.