Future JavaScript #1 — Arrow Functions

A series exploring next generation JavaScript syntax

Dimitar Valchanov
< Jedi JS >

--

Hey there, I hope you all have a nice and productive week.

Arrow functions are a new addition to the language that is supposed to be an alternative to the traditional functions, in some cases.

They are very simple and powerful, but with great power comes great responsibility, so you should know exactly when to and when not to use them.

In this guide I am going to cover several parts that I think are important for a complete understanding of arrow functions:

  • Syntax
  • What is different (compared to traditional functions)
  • Use cases
  • Rules
  • When to use / When not to use

Syntax

We all know and used the traditional function syntax a million of times:

function() {}

Let’s see what the new syntax looks like:

() => {}

You might think that it’s not worth using the new syntax, just to get rid of the `function` keyword.

Well, that’s not the only difference, keep reading.

What is different

Arrow functions have different logic compared to traditional ones. They have their advantages and disadvantages, and it’s important to understand them. Otherwise they might be very confusing to use.

Let’s go quickly through some points, to make sure you know the difference.

Arguments

Don’t rely on them, they are not exposed. It’s not good practice to rely on them anyway. Just use rest parameters (covered in a future guide).

this

Arrow functions take the context of the surrounding code. It’s inherited from the enclosing scope.

We all know that for traditional functions the context depends on where they are invoked.

For arrow functions the context depends on where they are declared.

call / apply / bind

Since the arrow functions take the surrounding context, these won’t really make a difference. Can still be used to pass parameters, but won’t change the context.

Prototype

Arrow functions don’t have a `prototype` property.

Constructor

Arrow functions cannot be used as constructors. For that just use traditional functions, they’re well known and familiar to everyone. Or better yet, use class.

Can’t be named

You can use arrow functions only as anonymous functions. Or said in a different way, only use as function expressions, not as declarations.

// NO
func() => {}
// YES
const func = () => {};

No hoisting

Since arrow functions can be used only as expressions, there is no hoisting. Meaning that you can use it, only after it has been assigned. On the contrary, traditional function declarations are hoisted and can be used before the actual declaration.

traditional(); // YES
arrow(); // Throws an Exception
function traditional() { console.log("ES"); }
const arrow = () => { console.log("ES"); };

Use cases

Let’s go through some examples on how you can actually use arrow functions. Let’s also see how a traditional function would be used compared to an arrow function, if comparable at all.

IIFE

Calling a function immediately after declaring it is a good practice for providing scope and not polluting the global object. The same can be achieved with arrow functions.

Just don’t forget to surround it with parenthesis. It looks confusing without ones.

// ES5
(function() {
...
})();
->// ES6
(() => {
...
})();

// NO
(() => {}());

Shorthand

The shorthand syntax is something you will fall in love with. It’s super conscious and easy to read and saves you some code. Essentially, you skip the parenthesis, and the `function` and `return` keyword.

// ES5
array.map(function(x) { return x * x });
->// ES6
array.map(x => x * x);

Remember:

  • you can skip the return statement only if using them on one line
  • use it only when you have one statement (which will be returned)

Class

Since the arrow function uses the surrounding context it’s safe to use it as a way to bind the function to the context of the class. No more worries when passing it as callback.

class Button {
constructor() {
super();
document.addEventListener("click", this._addClick, false);
}

clickCounter = 0;
_addClick = () => {
this.clickCounter += 1;
};
}

Remember:

  • if you want to pass different context, use traditional method

Async

Async Functions. This is a great and powerful new concept as well. Essentially, you use them when you have a Promise that needs to be resolved or rejected. By using them we don’t have to pass callback functions and the code looks much cleaner. (Stay tuned for a guide on these soon)

const getter = async (url) => {
return await http.get(url);
};

Returned functions

JavaScript‘s power comes from its functional nature, the fact that functions are first-class objects. Which means you can return them. And that’s a powerful pattern that is used often. You can use arrow functions to make it look even more impressive.

// ES5
function onPower(exponent) {
return function(number) {
return Math.pow(number, exponent);
};
}
const square = onPower(2);
square(5); // 25
->// ES6
// One line
const onPower = exponent => number => Math.pow(number, exponent);
// Multiple lines
const onPower = exponent => number => {
return Math.pow(number, exponent);
};

Rules

There are certain rules that, if followed, will make your life easier when using arrow functions.

One line

Skip the curly braces and the return statement. Skipping the `return` statement works only for single expression though, so be careful to not forget it otherwise.

array.map(x => x * x);

If you have anything other than a single expression, use within a block.

array.map(x => { if (typeof x === “number”) return x * x });

Multiple lines

Use with the curly braces and explicit return. Works like traditional functions, if you don’t use the `return` statement, the returned value is undefined.

array.map(x => {
if (x % 2 === 0) return x;
return x * x;
});

No parameters

Use with parenthesis. Using it without ones will break it.

let counter = 0;array.forEach(() => { 
counter += 1;
});

One parameter

Skip the parenthesis. Feel free to use them, but they are not needed and it looks better without ones.

let container = [];array.forEach(value => {
container.push(value);
});

Multiple parameters

Use with parenthesis. If not used it actually thinks that we are passing one parameter and one arrow function, separated by a comma. So if you have two or more parameters, always wrap them in parenthesis.

let obj = {};array.forEach((value, index) => {
obj[value] = index;
});

Returning an object

Surround with parenthesis. Object literal and arrow function both use blocks, so if you want to return an object with the shorthand syntax, surround it with parenthesis.

array.map(x => ({ foo: x }));

Or returning an empty object:

const empty = () => ({});

Arguments

Don’t use with arguments. Prefer the rest parameters syntax, since it better shows your intentions and let’s admit it… it looks sexier.

const add = (...numbers) => {
return numbers.reduce((x, y) => x + y);
};
// one line
const add = (...numbers) => numbers.reduce((x, y) => x + y);
add(1, 2, 3);

Side effects

Use with block. If you want to use it on one line but don’t really have anything to return, just surround the expression with curly braces to explicitly show that.

let counter = 0;array.forEach(x => { counter += 1; });

Using it without curly braces looks confusing. Do you want to return? And actually what are you returning? Just check that, doesn’t look good at all.

// NO
array.forEach(x => counter += 1);

When to use

The new arrow function is not one-size-fit-all type of function. So it’s important to know when it’s safe to use it.

  • When you want to use the surrounding context
  • When the context is not important
  • When the shorthand syntax makes sense

When not to use

It’s important to also know when not to use arrow functions.

  • Constructors
  • In the global scope
  • When you want to change the context on execution
  • When you want to use the prototype

That pretty much sums it up. I hope this was helpful to everyone who is just starting or wants to understand arrow function in more depth.

Table of contents

Next Article:

Async Functions

--

--

Dimitar Valchanov
< Jedi JS >

Frond-end developer, passionate about creating beautiful applications.