JavaScript Arrow Function

The arrow function was one of the features introduced in the ECMAScript 2015 specification (ES6) of JavaScript.

Arrow functions are a new way to write anonymous function expressions and are lexically scoped, meaning they have access to the enclosing function's variables, which makes them incredibly useful when passing a function as a parameter to a higher-order function, such as when you are looping over an array with built-in iterator methods.

The arrow function notation has several characteristics that differentiate it from traditional function expressions, we will discuss some of them aling with examples in this article.

TLDR;

Using arrow function syntax, the following traditional function expressions:

let example = function(a, b) {
   return a + b;
}

// or

function example(a, b){
    return a + b;
}
Traditional function expressions syntax

Can be written like this:

// using arrow functions
let example = (a, b) => a + b;
Arrow function syntax

Arrow Function Syntax

Before delving into the examples and specifics of arrow functions, its useful to understand the syntax.

let functionName = (arg1, arg2, ...argN) => {
    expression(s)
}

The syntax for an arrow function expression does not require the function keyword and uses a fat arrow => to separate the arguments(s) from the body.

  • functionName is a variable holding a refrance to access the function
  • arg1, arg2, ...argN  the function arguments
  • expression(s) the function body

Arrow functions can be defined with zero or more parameters, on one or more lines. If the body has a single statement or expression, you can make use of the implicit return and write an arrow function as:

let functionName = (arg1, arg2, ...argN) => expression

If there is only a single argument, you can drop the brackets () surrounding the arguments and write the arrow function as:

let functionName = arg => expression

Different ways of writing functions

To understand arrow functions, it can be helpful to understand the different ways of writing a function in JavaScript.

Function declaration

A traditional function declaration creates a named function and is defined with the function keyword, followed by the function name and the parameters in ().

function helloWorld(firstName, lastName){
	return "Hello " + firstName + ' ' + lastName + '!';
}

console.log(helloWorld('levi', 'putna'));
traditional function declaration

Function declarations are hoisted into the execution context before any code is executed, this is why you can access them before the have been declared.

console.log(helloWorld('levi', 'putna'));

function helloWorld(firstName, lastName){
	return "Hello " + firstName + ' ' + lastName + '!';
}

Function expression

A function expression is an anonymous function that can be assigned to a variable or passed as an argument to another function.

A function expression has almost the same syntax as a function declaration but differs from a function declaration in that it is not hoisted in the execution context and runs only when encountered in the code.

The above helloWorld function declaration can be written as a function expression like so:

const helloWorld = function (firstName, lastName){
	return "Hello " + firstName + ' ' + lastName + '!';
}

console.log(helloWorld('levi', 'putna'));

Arrow function

Arrow function syntax is similar to function expression syntax, except that the function keyword is replaced with a fat arrow =>, and moved after the argument declarations. In addition, Arrow function offer some syntactic sugar that allows for more concise and readable code.

Arrow functions have a few critical functional distinctions in the way they work that distinguish them from the other ways of writing a functions. The most significant functional difference is that arrow functions do not have their own this binding or prototype and, therefore, cannot be used as a class constructor.

The above helloWorld function can be written as an Arrow function like so:

const helloWorld = (firstName, lastName) => {
    return "Hello " + firstName + ' ' + lastName + '!';
}

console.log(helloWorld('levi', 'putna'));

Arrow Function Example

Many developers are familiar with traditional function declarations and expressions. Converting a few of these functions to arrow functions is an excellent way to understand the syntax and how they work.

If we use our `helloWorld` Function declaration above as an example.

function helloWold(firstName, lastName){
	return "Hello " + firstName + ' ' + lastName + '!';
}

The basic way to write this function with arrow function syntax is like this.

const helloWorld = (firstName, lastName) => {
    return "Hello " + firstName + ' ' + lastName + '!';
}

The body of a traditional function is contained within a block using curly brackets {} and ends when the code encounters a return keyword. Arrow functions introduce some syntactical sugar and implicit return, allowing the omission of the curly brackets {} and the return keyword when the function body is a simple return statement and can be written like this:

const helloWorld = (firstName, lastName) => "Hello " + firstName + ' ' + lastName + '!';

If there is only a single argument, we can reduce the syntax further by removing the parentheses around the arguments.

const helloWorld = fullName => "Hello " + fullName + '!';

If we want to pass a function as an argument to another function, this is where anonymous functions really shine. The arrow function no longer needs to be associated with a variable, and the arrow function can be written as a parameter of the other parent function.

document.addEventListener("click", () => "Hello " + "levi Putna" + "!");
This example adds a click event listener to the javascript document dom, executing the anonymous arrow function whenever the listener fires. 

Lexical this scope

Arrow functions are lexically scoped, meaning that they have access to the enclosing function's variables, including the this variable. Arrow functions don't have their own bindings to this.

We will create a class with two methods to highlight how the this keyword is scoped differently between a normal anonymous expression and an arrow function.

The methods will console.log() out a message including a name stored in the classes this.name variable after a few seconds using setTimeout().

The setTimeout() function takes in a callback as a parameter. In the first method, expressionSayHello() will pass a function expression into the setTimeout() function and in the second method, arrowSayHello() will pass an arrow function.

class HelloWorld {
  constructor(name) {
    this.name = name;
  }

  expressionSayHello() {
    setTimeout(function() {
      console.log("Hello " + this.name + "!");
    }, 2000);
  }

  arrowSayHello() {
    setTimeout(() => {
      console.log("Hello " + this.name + "!");
    }, 2000);
  }
}

If we instantiate the class and call the first expressionSayHello() method, you will notice that the message is missing the name value stored in this.name. This is because the scope of the this keyword for a function expression is defined within the function scope, not the surrounding scope. Function expressions are not lexically scoped and cannot access this from the surrounding function, instead having there own this.

let hello = new HelloWordl("Levi Putna");
hello.expressionSayHello();
Non lexical scope callback
'Hello !'
Output

Now, if we try this again but use the arrowSayHello() method that uses the arrow function callback, this time, we will see the name stored in this.name included in the message. The arrow function did not define its own inner this variable and had access to the value from the surrounding function.

let hello = new HelloWorld("Levi Putna");
hello.arrowSayHello();
Lexical scope callback
'Hello Levi Putna!'
Output

Limitations

Arrow functions have some limits and can’t be used in all situations:

  • They do not have their own bindings to this or super.
  • They cannot be used as constructors.
  • They cannot use yield, within its body.
  • They cannot use the special arguments keyword.

Arrow functions are always unnamed. If the arrow function needs to call itself recursively, use a named function expression instead. You can also assign the arrow function to a variable so it has a name and call that.

let makeGreaterThan = (a, b) => {
  console.log(a,b);
    if (a > b) {
        return a;
    } else {
        a++;
        return makeGreaterThan(a, b);
    }
};

console.log(makeGreaterThan(1, 10));
You've successfully subscribed to Twisted Brackets
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.