X
Popular Searches

Using Arrow Functions in JavaScript

Illustration showing various JAvaScript arrow function examples

An arrow function in JavaScript is a terser alternative to a traditional function. The syntax enables quick creation of inline anonymous functions.

Arrow functions were added with ES6 in 2015. They’re now supported by all major browsers. You can use them with older browsers, such as Internet Explorer, via a transpiler like Babel.

Creating Arrow Functions

The main advantage of arrow functions is how concise they make your code.

Here’s a traditional function:

function square(x) {
    return (x * x);
}

Here’s the same code rewritten as an arrow function:

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

They’re called “arrow functions” because of the arrow-like “=>” syntax.

Arrow functions are particularly useful when working with callbacks.

Here’s the traditional version:

function getNonEmptyStrings(arr) {
    return arr.filter(function (i) {
        return (i.length > 0);
    });
}

And here’s the inner function as an arrow function:

function getNonEmptyStrings(arr) {
    return arr.filter(i => (i.length > 0));
}
Advertisement

Arrow functions are much shorter than their traditional counterparts. You can omit the function prefix. They enable clean one-line statements and have an implicit return statement.

Handling Arguments

Arrow functions accept a few different forms of arguments:

const noArgs = () => alert("I've no arguments");
 
const oneArg = arg => alert(`My arg is ${arg}`);
 
const multipleArgs = (arg1, arg2) => alert(`Args: ${arg1} ${arg2}`);

When using a single argument, no parentheses are needed. When there are multiple arguments, use parentheses and commas in the same style as a classic function declaration. Arrow functions which accept no arguments require an empty pair of parentheses so the syntax is valid.

Working With Object Literals

You can return object literals from inline arrow functions but they must be wrapped in parentheses. This is a requirement of the parser.

const func = () => ({foo: "bar"});

You can destructure an object literal into arguments using the same parentheses-wrapped form:

const func = ({foo}) => foo;
// func({foo: "bar"}) returns "bar"

Omitting the parentheses creates a syntax error in both cases.

Returning Values

All our examples so far have been single-line functions with an implicit return statement.

You can write arrow functions that span multiple lines:

function getNonEmptyStrings(arr) {
    return arr.filter(i => {
        const trimmed = i.trim();
        const replaced = trimmed.replace(/[^A-Za-z]/g, "");
        return (replaced.length > 0);
    });
}

When writing a multi-line arrow function, wrap its body block in brackets in the same way you would a traditional function. It’s not possible to use an implicit return; you must revert to a return statement.

Lexical Binding

Aside from syntax, the presence of lexical binding is one of the biggest differences between arrow functions and classical function expressions.

Advertisement

A traditional function has this bound to refer to itself. Arrow functions are always anonymous. this is bound to the same value as the this within the block defining the arrow function.

class Example {
 
    constructor() {
        this.demo = "foobar";
    }
 
    classic(arr) {
        return arr.filter(function (a) {
            return (a === this.demo);
        });
    }
 
    arrow(arr) {
        return arr.filter(a => (a === this.demo));
    }
 
}
 
const ex = new Example();
 
// no matches
ex.classic(["foobar"]);
 
// matches
ex.arrow(["foobar"]);

The ability of arrow functions to use the this of the parent scope means you don’t need to use bind() when using them in classes. Combined with the terser syntax, this makes arrow functions the ideal form when writing callbacks. They’re best in throwaway scenarios where the outside context is more important than the function’s own identity.

As a consequence of the lexical binding, arrow functions shouldn’t be used with the call, apply and bind functions. These functions are used to execute a function within a specific scope. They aren’t relevant to arrow functions as this will always be set to the scope the expression is defined in.

Characteristics

Arrow functions have a few other differences compared to explicitly-defined function expressions.

Arrow functions have no constructor so you can’t create instances of them with the new keyword. They’ve no prototype either – the prototype property will be undefined.

Advertisement

Unlike regular functions, you can’t use the arguments local variable. It will be undefined. You must access the values of arguments directly by name.

Using an arrow function as a generator isn’t supported. Trying to use the yield keyword within an arrow function’s body will raise an error.

A final thing to watch for is the parsing order. Arrow functions get unique treatment within conditional expressions, so take note of the output in the following scenarios:

const value = false;
 
// Traditional function; all OK
let result = (value || function () {});
 
// Arrow function - throws a `SyntaxError`
let result = (value || () => null);
 
// Arrow function; all OK
let result = (value || (() => null));

When creating an arrow function in a conditional expression, wrap it in parentheses to ensure it’s evaluated correctly. Some of the rules of operator precedence are overridden, as illustrated by the second example.

Conclusion

Using arrow functions makes your code more concise and reduces boilerplate. Their attributes, including anonymity and lexical binding, considerably simplify scenarios such as callbacks. Simultaneously, those same attributes mean they aren’t a drop-in replacement for every traditional function.

Besides their limitations, some JavaScript developers consider that arrow functions can make code less readable and trickier to maintain. The complete lack of keywords, such as function and return, makes it possible to overlook their presence while skimming code. The syntax is more opaque to new developers who may not be fully aware of its idiosyncrasies.

Advertisement

It’s therefore a good idea to think how you’ll use arrow functions in your codebase. Often, that means writing callbacks in arrow form while sticking with alternative syntax for key top-level functions.

James Walker James Walker
James Walker is a contributor to CloudSavvy IT. He is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows, using technologies including Linux, GitLab, Docker, and Kubernetes. Read Full Bio »

The above article may contain affiliate links, which help support CloudSavvy IT.