Arrow Function Expressions - The Basics

stlnick

Nick

Posted on July 13, 2020

Arrow Function Expressions - The Basics

ES6, or ECMAScript 2015, brought us many great changes to the language of JavaScript. One such feature is Arrow Functions. That's what we'll dive into in this article - get to reading!

What is an Arrow Function Expression?

An Arrow Function Expression is simply a new syntax to write function expressions as we've done before. In a lot of cases it can be much shorter to write which is great. If your task requires less typing you'll get more done in the same amount of time!

Granted you understand it - which I most certainly did not in my first encounters with the mighty =>.

Here's a very basic example (which there will be more of):

/* * These are equivalent * */
// ES5
function func() {
  console.log('Function Expression example.')
}
func()
// ES6
const arrow = () => {
  console.log('Arrow Function Expression.')
}
arrow()
Enter fullscreen mode Exit fullscreen mode

There are multiple aspects to the usage of Arrow Functions such as when it's best to use and not use them and some small gotchas on when they behave differently than an normal Function Expression. We won't cover them all here but we will cover some of the bigger ones.

Which is better?

Wrong question to ask. You can accomplish your task using either method. People have opinions on this subject all around - but lets face it that will never change.

However, there are certain scenarios where a strong case can be made to use or not use an Arrow Function.

When to Use

  • When your resulting code will be shorter and more concise
  • When your code becomes more readable
  • When you feel like it!

When not to Use

  • As an object method directly
  • When your resulting code isn't any shorter or concise
  • When your code can become more confusing or hard to read and understand
  • When you do not feel like it!

Keep in mind it largely comes down to personal preference on what you believe is more readable, more concise, less confusing, etc. As mentioned there are a couple things to be aware of when choosing which route to take.

Arrow Function: To code or not to code?

One big item to know is Arrow Functions are lexically bound. Let's take a look at a couple examples first:

// ES5
var person = {
  name: 'Foo Bar',
  logName: function logName() {
    setTimeout(function() {
      console.log(this.name)
    }.bind(this), 1000)
  }
}
Enter fullscreen mode Exit fullscreen mode

In order for the logName() method to actually log out the name property we must ensure the setTimeout binds this. We see that it is chained at the end of setTimeout - if we did not do that then this would actually be undefined since the callback function of setTimeout does not have its own this. We explicitly bind it to the this of logName which is of course person.

// ES6
var person = {
  name: 'Foo Bar',
  logName: function logName() {
    setTimeout(() => {
      console.log(this.name)
    }, 1000)  // No binding of 'this'
  }
}
Enter fullscreen mode Exit fullscreen mode

This time with Arrow Functions we do not have to ensure binding of this to properly log it. IN THIS SCENARIO. (Gotcha comin' up...)

The catch here is that Arrow Functions cannot be bound to a this so it will go up in scope to find the value of this in the context which it was called - AKA lexically bound.

It's not recommended to use Arrow Functions directly as object methods like the following:

var person = {
  name: 'Foo Bar',
  logName: () => {  // => instead of function expression
    setTimeout(() => {
      console.log(this.name)
    }, 1000)
  }
}
Enter fullscreen mode Exit fullscreen mode

Now this.name will return undefined. It's a little confusing but a key thing to remember is that again an Arrow Function will not have a this attached to itself. I like to think of Arrow Functions just passing the buck.

The console.log tries to evaluate this so it goes to setTimeout

  • it says "I don't know a this maybe try to ask logName"
  • we get to logName (which is also defined as an => so it cannot bind a this) and ask and it says "this doesn't ring a bell, maybe ask person?"
  • now we ask person and it's like "Hey I don't have any property called this I can't help."

Eventually we get all the way to the global Window object because that's the default when this isn't bound to anything in the context which we called it.

Why use an Arrow Function?

Okay, that was confusing. this in relation to Arrow Functions is arguably the hardest thing to grasp. So let's talk about something easier to grasp and results in some clean looking code.

As mentioned, Arrow Functions can result in much shorter, cleaner code for us and especially in short function definitions or when using map or reduce.

Let's take a look.

// ES5
function addTwoNums(x, y) {
  return x + y
}

// ES6
const addTwoNums = (x, y) => {
  return x + y
}
Enter fullscreen mode Exit fullscreen mode

Doesn't look any shorter in ES6, right? One great feature of Arrow Functions is if we have one statement and want to return we don't use the {} or the return keyword.

const addTwoNums = (x, y) => x + y
Enter fullscreen mode Exit fullscreen mode

There's an implicit return when no braces or return is present with an Arrow Function. This really helps when it comes to the readability of a map or reduce usage.

const nums = [1, 2, 3]

const doubledNums = nums.map(num => num * 2)
Enter fullscreen mode Exit fullscreen mode

You may have noticed this time when using the Arrow Function in map we didn't put parenthesis around the parameter.

When there is only one parameter for an Arrow Function the parenthesis are optional.

So parenthesis can be optional and in certain scenarios the brackets and return can be omitted providing us multiple ways to write the same code. Going for the shortest code is usually what I would do - but again, personal preference.

/* * These are ALL equivalent * */
// ES5
const doubledNums = nums.map(function(num) {
  return num * 2
})
// ES6
const doubledNums = nums.map((num) => {
  return num * 2
})
// ES6: No brackets - implicit return
const doubledNums = nums.map((num) => num * 2)
// ES6: Single parameter - no parameter parenthesis
const doubledNums = nums.map(num => num * 2)
Enter fullscreen mode Exit fullscreen mode

Arrow Functions have more characteristics and gotchas than listed here but these are the basics I focused on to get a grasp. Once you get an understanding of these concepts take a peek at the MDN Arrow Functions page. You'll find plenty more use cases and confusing things to learn!

Don't get me wrong - I keep saying confusing because it's true! I learn more each day - new things I hadn't heard of or known and how my understanding of something wasn't quite right. Let me know in the comments if there's an explanation here that isn't quite right or if there's an interesting gotcha you know of.

I still love JavaScript and this Tweet speaks to that pretty well!

💖 💪 🙅 🚩
stlnick
Nick

Posted on July 13, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Scope in JavaScript
javascript Scope in JavaScript

July 17, 2020