Arrow Functions in JavaScript

The ECMAScript 2015 (ES6) standard introduced “arrow functions” to JavaScript, which on the surface looks like syntactic sugar over JavaScript functions, but have some subtle—but useful—differences.

First and foremost, you can use arrow functions anywhere you would use an anonymous function. For instance, in a timeout:

setTimeout(function() {
  console.log('Timeout reached')
}, 500)

In ES2015, the above can be written as:

setTimeout(() => {
    console.log('Timeout reached')
}, 500)

NOTE: In the above examples, we have no parameters (), but if we had only one parameter, we could leave off the parenthesis and that would still be valid. With arrow functions, zero or more than one parameter requires parenthesis.

Inherited Context

So we reduced the first example by five characters, and while that’s a welcome enough change when you’re working in JavaScript all day, that’s not the only benefit you gain from using arrow functions. Before I get into what those are, what do you think the following example prints out?

function myFunc() {
  setTimeout(function() {
    console.log('My name is: ' + this.name)
  }, 500)
}

myFunc.call({ name: 'Jonathan' })

// My name is undefined

“My name is: undefined” is probably not what the code had intended to output, but that’s what happened. To get the function that is passed to setTimeout() to inherit its parent’s context (e.g. this), you need to bind that function to the context:

function myFunc() {
  setTimeout((function() {
    console.log('My name is: ' + this.name)
  }).bind(this), 500)
}

myFunc.call({ name: 'Jonathan' })

// My name is Jonathan

The difference may be hard to spot, but it’s there. This time we pass a bound function to setTimeout() instead, which entails wrapping the function in parenthesis and calling bind(this). With arrow functions, none of that is necessary because they automatically inherit the surrounding context (this of its parent scope):

function myFunc() {
  setTimeout(() => {
    console.log('My name is: ' + this.name)
  }, 500)
}

myFunc.call({ name: 'Jonathan' })

// My name is Jonathan

Not only is that less typing than the bound function example, it’s also more intuitive, which ultimately translates into less bugs. In fact, with arrow functions, you should rarely (if ever) need to use bind(this) ever again. As another example, a common scenario is to pass a bound function as a listener:

myObj.on('click', this.handleClick.bind(this))

You can use an arrow function instead to make the code more natural:

myObj.on('click', event => this.handleClick(event))

In the above example, we are actually passing the listener as an arrow function and all it is doing is forwarding the parameter (event) to the real handler function (this.handleClick()). In this case, arrow functions do not decrease the lines of code, however, by being explicit about the arguments we pass to the function, the code is more “honest” and thus more clear.

Implicit Return

Another subtle difference in behavior is that arrow functions can have an implicit return when written as an expression (no curly braces, which would be a block):

myArray.map(x => x * 2)

Is the same as…

myArray.map(function(x) {
  return x * 2;
})

This particular feature is “syntactic sugar” that makes your code more compact by allowing the omission of the return statement, a bit like CoffeeScript and Ruby, but not exactly because this only works in cases where the only thing in the function body is the return statement. Personally, I think that was a good decision because allowing implicit returns with other logic would just make code more unreadable in my opinion.

And that’s about it! While not much, it turns out arrow functions provide a little more than just simple syntactic sugar. The subtle changes in behavior are welcome additions to JavaScript, and contribute to a more pleasant coding experience overall.

HostGator $3.96

$3.96 for web hosting – used by jonbeebe.net. Get started today.