Like almost every modern programming language, JavaScript offers ways to create functions to allow for code reusability. For those who are unfamiliar with the language, there are two different ways to declare a function in JavaScript.

Originally, the function keyword was used, and later, the arrow syntax was created. Though differing in scope accessibility, both the function keyword and the arrow syntax create similar results.

The inability to distinguish between the two may cause issues and lead to incorrect usage. It is a necessity for the modern JS developer to understand how each function declaration affects scope and visibility.

What Are JavaScript’s Arrow Functions?

JavaScript’s arrow functions are an alternative way to define functions that don’t use the default function keyword:

        function logMessage(message) {
    console.log(message);
}

const logMessage = (message) => {
    console.log(message);
}

const logMessage = message => console.log(message);

Above, you can see the same message written in three different ways. The first uses the regular function declaration method. The next two show the two ways to use ES6’s arrow functions.

JavaScript first adopted the arrow function syntax with the release of the ECMAScript 2015 standards. Arrow functions offered a clean and concise way to quickly create functions and an interesting solution to several long-running scope issues within JavaScript.

These features quickly made arrow functions a hit among many developers. Programmers looking into a modern JavaScript codebase are just as likely to find arrow functions as regular functions.

How Are Arrow Functions Different From Regular Functions in JavaScript?

At first glance, arrow functions don’t appear to differ much from functions declared using the function keyword. Outside of syntax, both encapsulate a reusable set of actions that can be called from elsewhere in the code.

Despite the similarities between the two, however, there are some differences that developers need to be aware of.

Difference in Scope

Whenever a regular function is declared in JavaScript, that function works as a closure that creates its own scope. This can cause an issue when using certain specialized functions like setTimeout and setInterval.

Prior to ES6, there were a significant number of workarounds that would hoist items to higher levels of scope for use in callbacks. These hacks worked, but they were often difficult to understand, and could cause problems with certain variables getting overwritten.

Arrow functions solved both issues in a simple and elegant way. When an arrow function is used as a part of a callback, it has access to the same scope as the function from which it was called.

This allowed functions to be used as callbacks without losing access to the context in which the original event was called. As a simple test to show this principle in action, you can set up a delayed messaging function like the one below:

        function delayedMessage(message, delay) {
 
    setTimeout(function(message) {
        console.log(message);
    }, delay);

}

delayedMessage("Hello World", 1000);

The function is simple, accepting message and delay in milliseconds. After the delay is passed, it should log the message to the console. However, when the code is executed, undefined will be logged to the console instead of the message that was passed in.

When the callback function is executed, the scope changes and the original message is no longer accessible. Using the message from within the closure requires the message to be hoisted to a global variable, which could result in it being overwritten before the callback.

An arrow function is a proper fix for this issue. If you replace the first argument for setTimeout, the scope can be maintained, and the function will have access to the message passed to delayedMessage.

        function delayedMessage(message, delay) {
 
    setTimeout(() => {
        console.log(message);
    }, delay);

}

delayedMessage("Hello World", 1000);

Now, when executed, the delayedMessage function will log the message. Additionally, this allows multiple messages to be queued up with different delays, and they will all still occur at the correct time.

This is due to the fact that each time delayedMessage is used, it generates its own scope with its own copy of the internal arrow function.

Code Readability

While arrow functions are useful as callbacks, they are also used to keep code clean and concise. In the above section, you can see that using an arrow function makes it readily obvious what will happen when the delayedMessage function is called.

As functions become more complex, having a little clarity can make code far more readable. When composing objects, this can make code simpler when adding short functions:

        class counter {
    _count = 0;
    increment = () => {
        this._count += 1;
    }
    decrement = () => {
        this._count -= 1;
    }
    count = () => {
        return this._count;
    }
}

let ct = new counter();

Role in Object Oriented Programming

While JavaScript’s arrow functions are an integral part of functional programming, they do also have a place in object-oriented programming. Arrow functions can be used within class declarations:

        class orderLineItem {
    _LineItemID = 0;
    _Product = {};
    _Qty = 1;

    constructor(product) {
        this._LineItemID = crypto.randomUUID();
        this._Product = product
    }

    changeLineItemQuantity = (newQty) => {
        this._Qty = newQty;
    }
}

Using an arrow function to declare methods within a class declaration doesn’t change the function’s behavior within the class. Outside the class, however, it does expose the function, allowing it to be used by other classes.

Regular functions cannot be accessed outside the class declaration without being called. This means that while other class declarations can inherit these functions, they cannot be accessed directly to compose other classes.

When Should You Use JavaScript’s Arrow Functions?

JavaScript’s arrow functions are an incredibly powerful feature that gives developers far more control over what scope a function has access to. Knowing when a callback should have access to its parent’s scope, and when it shouldn’t, can help a developer determine which type of declaration to use.

Arrow functions give programmers a clear and concise way to write callbacks without exposing parts of the scope that should be hidden. They also allow for the creation of clean and simple compositions, further enabling functional programming in JavaScript.

JS developers must be aware of the differences between the two syntaxes and be mindful of which syntax is appropriate when declaring their functions.