JavaScript ES6 for Beginners #2: Arrow functions & default arguments

4 minute read

ES6-card-2

 

What is an arrow function

ES6 introduced fat arrows (=>) as a way to declare functions. This is how we would normally declare a function in ES5:

var greeting = (function(name) {
  return "hello " + name;
})

The new syntax with a fat arrow looks like this:

const greeting = (name) => {
  return `hello ${name}`;
}

We can go further, if we only have one parameter we can drop the parenthesis and write:

const greeting = name => {
  return `hello ${name}`;
}

If we have no parameter at all we need to write empty parenthesis like this:

const greeting = () => {
  return "hello";
}

 

Implicitly return

With arrow functions we can skip the explicit return and return like this:

const greeting = (name) => `hello ${name}` ;

Let’s say we want to implicitly return an object literal, we would do like this:

const race = "100m dash";
const runners = [ "Usain Bolt", "Justin Gatlin", "Asafa Powell" ];

const winner = runners.map(( runner, i) =>  ({ name: runner, race, place: i + 1}));

console.log(winner);
// 0: {name: "Usain Bolt", race: "100m dash", place: 1}
// 1: {name: "Justin Gatlin", race: "100m dash", place: 2}
// 2: {name: "Asafa Powell", race: "100m dash", place: 3}

To tell JavaScript that what’s inside the curly braces is an object literal that we want to implicitly return, we need to wrap everything inside parenthesis.

Writing race or race:race is the same.

 

Arrow functions are anonymous

As you can see from the previous examples, arrow functions are anonymous.

If we want to have a name to reference them we can bind them to a variable:

const greeting = (name) => `hello ${name}`;

greeting("Tom");

 

Arrow function and the this keyword

You need to be careful when using arrow functions in conjunction with the this keyword, as they behave differently from normal functions.

When you use an arrow function, the this keyword is inherited from the parent scope.

This can be useful in cases like this one:

// grab our div with class box
const box = document.querySelector(".box");
// listen for a click event 
box.addEventListener("click",function() {
  // toggle the class opening on the div
  this.classList.toggle("opening");
  setTimeout(function(){
    // try to toggle again the class
    this.classList.toggle("open");
    })
})

The problem in this case is that the first this is bound to the const box but the second one, inside the setTimeout, will be set to the Window object, trowing this error:

Uncaught TypeError: cannot read property "toggle" of undefined 

Since we know that arrow functions inherit the value of this from the parent scope, we can re-write our function like this:

// grab our div with class box
const box = document.querySelector(".box");
// listen for a click event 
box.addEventListener("click",function() {
  // toggle the class opening on the div
  this.classList.toggle("opening");
  setTimeout(() => {
    // try to  toggle again the class
    this.classList.toggle("open");
    })
})

Here, the second this will inherit from its parent, and will be therefore set to the const box.

 

When you should avoid arrow functions

Using what we know about the inheritance of the this keyword we can define some instances where you should not use arrow functions.

The next 2 examples all show when to be careful using this inside of arrows.

const button = document.querySelector("btn");
button.addEventListener("click", () => {
  // error: *this* refers to the window 
  this.classList.toggle("on");
})
const person = {
  age: 10,
  grow: () => {
    // error: *this* refers to the window
    this.age ++,
    },
}

Here’s another example of when you should use a normal function instead of an arrow.

const orderRunners = () => {
  const runners = Array.from(arguments);
  return runners.map((runner, i) => {
    return ` #{runner} was number #{i +1}`;
    })
    console.log(arguments);
}

This code will return:

ReferenceError: arguments is not defined

We don’t have access to the arguments object in arrow functions, we need to use a normal function.

 

Default function arguments

ES6 makes it very easy to set default function arguments. Let’s look at an example:

function calculatePrice(total, tax = 0.1, tip = 0.05){
// When no value is given for tax or tip, the default 0.1 and 0.05 will be used 
return total + (total * tax) + (total * tip);,
}

What if we don’t want to pass the parameter at all, like this:

// The 0.15 will be bound to the second argument, tax even if in our intention it was to set 0.15 as the tip
calculatePrice(100, 0.15)

We can solve by doing this:

// In this case 0.15 will be bound to the tip
calculatePrice(100, undefined, 0.15)

It works, but it’s not very nice, how to improve it?

With destructuring we can write this:

const Bill = calculatePrice({ tip: 0.15, total:150});

We don’t even have to pass the parameters in the same order as when we declared our function, since we are calling them the same way as the arguments JavaScript will know how to match them.

Don’t worry about destructuring, we will talk about it in Part 4.


 

This was the second part of my ES6 for beginners course, check out the rest of them here.

You can also read this articles on medium, on my profile.

Thank you for reading.

Leave a Comment