This article is designed as a “get started” read for beginner to intermediate developers who wish to gain a better understanding of ES6 arrow functions.
As part of my “Getting to Grips with ES6” series, I aim to create a reference containing straight forward explanations ordered in short sections to help us understand and apply these concepts directly to our current development processes.
Written by Andrew Hill. You can find me on LinkedIn, Twitter, Instagram, and GitHub.
Arrow functions (often referred to as fat arrow functions or lambda functions) are a concise way of writing functions that do not rebind context (this
) within other functions. Their short syntax is further enhanced by their ability to return values implicitly in one line, single return functions.
Arrow functions can never be named and as such, are always considered anonymous functions.
Until now, JavaScript has made use of the following syntax for anonymous functions:
var double = function(a) {return a * 2;}
double(100);
// Output: 200
There are a wide variety of syntaxes available to arrow functions; a full list of which is available on EcmaScript.org. The examples in this article will make use of the most common ones in the interest of brevity.
With the introduction of arrow functions, we can rewrite the given example as follows:
const double = (a) => {return a * 2;}
double(100);
// Output: 200
Parenthesis are required around arrow functions with multiple parameters:
const add = (a, b) => {return a + b;}
add(100, 200);
// Output: 300
Parenthesis are also required in arrow functions that do not contain any parameters at all:
const fullName = () => {return 'Andrew Hill';}
name();
// Output: Andrew Hill
Functions that contain a single parameter allow us to omit the parenthesis from the function definition all together:
const double = a => {return a * 2;}
double(100);
// Output: 200
Our example is a simple one line function that returns a value immediately. In a use case such as this, we are allowed to omit the functions curly braces in order to implicitly return its result:
const double5 = a => a * 2;
double5(100);
// Output: 200
Traditionally, if we wanted to return an object literal we would write our function as follows:
var animal = function(type, name) {return {type: type,name: name,}}
animal('dog', 'Damon');
// Output: Object {type: "dog", name: "Damon"}
Switching to an arrow function and attempting to implicitly return the object will result in errors as the engine is unable to tell if the curly braces belong to the object or the function block:
const animal = (type, name) => { type: type, name: name };
animal('dog', 'Damon');
// Output: [SyntaxError] Unexpected token :
To get around this we can tell the arrow function we are implicitly returning the object by wrapping the object in an additional set of parenthesis:
const animal = (type, name) => ({ type: type, name: name });
animal('dog', 'Damon');
// Output: Object {type: "dog", name: "Damon"}
As a side note, keys and values that share a name in ES6 can be written in shorthand as follows:
const animal2 = (type, name) => ({ type, name });
animal2('dog', 'Damon');
// Output: Object {type: "dog", name: "Damon"}
It’s beautiful!
Let’s take a look at the way traditional functions handle context by binding an event listener to a button and logging this
on click:
var button = document.getElementById('contextButton');
button.addEventListener('click', function(){console.log(this);});
// Output on click: <button id="contextButton">...</button>
In this example, the traditional function bound the value of this
to the element that the event listener is assigned to.
Consider the following example using an arrow function instead:
const button = document.getElementById('contextButton');
button.addEventListener('click', () => {console.log(this);});
// Output on click: Window { ... }
Here we illustrate how arrow functions make use of lexical binding to inherit the value of this
from the parent context. Since this code exists in the global scope, its parent (and the result of this
) is the Window {}
object.
One final nuance to consider is that the arguments
object is not available to arrow functions.
In the following example, we make use of a traditional function to return an array of “argument values” using the arguments
object:
var test = function(a, b) {return arguments;}
test(100, 200);
// Output: [100, 200]
Attempting the same thing using an arrow function results in errors:
const test = (a, b) => arguments;
test(100, 200);
// Output: [Uncaught ReferenceError] arguments is not defined
Finally, it is important to realise that even though arrow functions are beautiful, short and concise, they are not here to replace traditional functions. Their use of lexical binding means there are definite use cases where we should and should not be using arrow functions.
Thanks for reading. If you liked this article please consider supporting future installations of this series by recommending it. 👏