Barbarian Meets Coding
barbarianmeetscoding

WebDev, UX & a Pinch of Fantasy

22 minutes readjavascriptmancy

Mastering the Arcane Art Of JavaScript-Mancy for C# Developers: ES6 Arrow Functions

A young wizard practices the fine wizardring arts with mixed success throwing fireballs at her master.
An in-depth view into ES6/ES2015 arrow functions, how to use them and detailed information about their gotchas

The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.

It is time to continue upgrading your JavaScript-Fu to the next level of mastery! This time I present to you ES6 arrow functions, a way to bring the beauty of C# lambda expressions to JavaScript.

The Arrow Function

Hi! You can experiment with all the examples in this article directly within this jsBin.

Arrow functions are a great feature in ES6 that feel very much like C# lambda expressions. They give you a beautiful and terse syntax that let’s you write:

> let createWater = mana => `${mana} liters of water`;
> console.log(createWater(10));
// => 10 liters of water

Instead of the more lengthy anonymous function expression:

let createWater = function(mana) {
  return `${mana} liters of water`
}

The above example of arrow function has an implicit return statement that returns the expression to the right of the fat arrow =>. This simple notation is only valid if the body of your arrow function has a single statement.

Like in C# you’ll often see arrow functions used in conjunction with array methods such as filter (the JavaScript version of LINQ’s Where):

let monsters = ['orc chieftain', 'orc grunt', 'small orc', 'goblin']
let orcs = monsters.filter(m => m.includes('orc'))
console.log(orcs)
// => ["orc chieftain", "orc grunt", "small orc"]

You can define arrow functions with any number of arguments. For instance, you can have no arguments at all:

> let helloMiddleEarth = () => "hello Middle Earth!";
> console.log(helloMiddleEarth());
// => hello Middle Earth!

Or one:

let frodo = {
  toString() {
    return 'Frodo'
  },
  destroyTheOneRing() {
    console.log(`${this} throws the one ring into the entrails of Mount Doom`)
  },
  hideFrom(enemy, how) {
    console.log(`${this} hides from the ${enemy} ${how}`)
  },
}

let destroyDaRing = hobbit => hobbit.destroyTheOneRing()

destroyDaRing(frodo)
// => Frodo throws the one ring into the entrails of Mount Doom

Two:

let nazgul = {
  toString(){ return 'scary nazgul';}
};
let useElvenCloak = (hobbit, enemy)
    => hobbit.hideFrom(enemy, 'with an elven cloak');

useElvenCloak(frodo, nazgul);
// => Frodo hides from the scary nazgul with an elven cloak

Or as many arguments as you want using the rest syntax:

useElvenCloak = (hobbit, ...enemies)
    => hobbit.hideFrom(enemies, 'with an elven cloak');
useElvenCloak(frodo, nazgul, 'orc', 'troll');
// => Frodo hides from the scary nazgul,orc,troll with an elven cloak

Because they are just functions you can also use defaults (in this case hobbit=frodo):

destroyDaRing = (hobbit = frodo) => hobbit.destroyTheOneRing()
destroyDaRing()
// => Frodo throws the one ring into the entrails of Mount Doom

And destructuring:

let companyOfTheRing = {
  smartestHobbit: frodo,
  wizard: 'Gandalf',
  ranger: 'Aragorn',
  // etc
}
destroyDaRing = ({ smartestHobbit }) => smartestHobbit.destroyTheOneRing()

destroyDaRing(companyOfTheRing)
// => Frodo throws the one ring into the entrails of Mount Doom

If the body of your arrow function has more than one statement then you’ll need to wrap it inside curly braces just like you would do in C#:

let eatRation = (hobbit, rations) => {
  let ration = rations.shift()
  if (ration) {
    hobbit.hp += ration.hp
    console.log(`${hobbit} eats ${ration} and recovers ${ration.hp} hp`)
  } else {
    console.log(`There are no rations left! We're all gonna die!!!!`)
  }
}

let rations = [
  {
    name: 'sandwich',
    hp: 5,
    toString() {
      return this.name
    },
  },
]

eatRation(frodo, rations)
// => Frodo eats sandwich and recovers 5 hp

In this case when you have more than one statement you need to return a value explicitly:

let carveWood = (wood, shape) => {
  console.log(`You carve a piece of ${wood} into a ${shape}`)
  return { name: shape, material: wood }
}
let pipe = carveWood('oak', 'pipe')
// => You carve a piece of oak into a pipe

An arrow function can also return an object via the object initializer syntax. When returning a new object like this, you’ll need to wrap it inside parenthesis (so that the JavaScript runtime understands that it is indeed an object and not a block of code):

let createHealthPotion = () => ({
  name: 'potion of health',
  hp: 10,
  toString() {
    return `${this.name} (+${this.hp}hp)`
  },
})
let healthPotion = createHealthPotion()
console.log(healthPotion.toString())
// => potion of Health (+10 hp)

Arrow Functions Arcana

Arrow functions provide a terser syntax that function expressions but as functions themselves they are a little bit special, and when I say a little I mean a lot:

  • They don’t have this
  • They don’t have an arguments object
  • You cannot use bind, apply and call to set the context in which they are evaluated
  • You cannot use new nor super

Indeed if you look at the ECMA-262 spec:

14.2.16 Arrow Functions - Runtime Semantics: Evaluation

An ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

Read more within the spec at http://bit.ly/es6-spec

But what does it mean for arrow functions to not have their own version of this nor arguments?

Well it means that when you refer to this or arguments within an arrow function you are actually referring to this or arguments in the enclosing environment. Let’s clarify this with an example.

Let’s say that we have gollum that wants to be pleasant before he stabs you in the back and steals your wedding ring. If we use normal functions to define his greetings:

let gollum = {
  name: 'Golum! Golum!',
  toString() {
    return `${this.name}!!!`
  },
  saysHi() {
    console.log(`Hi! I am ${this}`)
    setTimeout(function() {
      console.log(`${this} stabs you in the back and 
steals your wedding ring while saying 'My Preciouuuuuus'`)
    }, /*waitPeriodInMilliseconds*/ 500)
  },
}

Then call the function saysHi:

// call it in the context of the gollum object
gollum.saysHi();
// => Hi! I am Gollum! Gollum!!!!
// => "[object Window] stabs you in the back and
steals your wedding ring while saying 'My Preciouuuuuus'"
/ => Hi! I am Gollum! Gollum!!!!

As we expected gollum happily salutes us and after a while stabs us in the back. The only problem being that it is gollum no longer but the Window object. Nothing new. We learned about this strange behavior in Mastering the Arcane Art of JavaScript-mancy for C# Developers - Chapter 1: The Many a One JavaScript Quirks. But what happens if we use an arrow function instead of a normal function?

// what happens if we use an arrow function instead?
let gollumWithArrowFunctions = {
  name: 'Golum! Golum!',
  toString() {
    return `${this.name}!!!`
  },
  saysHi() {
    console.log(`Hi! I am ${this}`)
    setTimeout(
      () =>
        console.log(`${this} stabs you in the back and 
steals your wedding ring while saying 'My Preciouuuuuus'`),
      /*waitPeriodInMilliseconds*/ 500
    )
  },
}

In this example we have redefined the saysHi function to use an arrow function as callback within setTimeout instead of a normal function. And if we call it:

gollumWithArrowFunctions.saysHi();
// => Hi! I am Gollum! Gollum!!!!
// => Golum! Golum!!!! stabs you in the back and
steals your wedding ring while saying 'My Preciouuuuuus'

The arrow function guarantees that the right version of this is used. What is happening? Because the arrow function doesn’t have its own version of this it accesses the this defined by the saysHi method (effectively behaving like a closure). Because saysHi was called using the dot notation gollumWithArrowFunctions.saysHi then the object itself is the value of this and thus eveything works and we die an ignominious death at Gollum’s hands.

What are the consequences of this? You may be asking yourself. Well, the most exciting consequence of an arrow function not having its own this is that it makes them more resistant to the problems with this that you saw in The Many A One JavaScript Quirks

Let’s bring this concept home using yet another example, the same one we used in The Many A One JavaScript Quirks:

function UsersCatalogJQuery() {
  'use strict'
  var self = this

  this.users = []
  getUsers()

  function getUsers() {
    $.getJSON('https://api.github.com/users').success(function(users) {
      // console.log(users);
      // console.log(this);
      try {
        this.users.push(users)
      } catch (e) {
        console.log(e.message)
      }
      // BOOOOOOOM!!!!!
      // => Uncaught TypeError: Cannot read property 'push' of undefined
      // 'this' in this context is the jqXHR object
      // not our original object
      // that's why we usually use a closure here instead:
      // self.products = products;
    })
  }
}
var catalog = new UsersCatalogJQuery()

Again, if you substitute the anonymous function expression for an arrow function you solve the problem! And in a much elegant way than binding the function explicitly (with bind) or by using a closure (var self = this):

function UsersCatalogJQueryArrowFunction() {
  'use strict'
  this.users = []
  this.getUsers = function getUsers() {
    $.getJSON('https://api.github.com/users').success(users =>
      this.users.push(users)
    ) // hello arrow function
    // this is mostly equivalent to:
    // .success(function(users){return this.users.push(users);}.bind(this))
  }

  this.getUsers()
}
var catalog = new UsersCatalogJQueryArrowFunction()

Arrow Functions And This Gotchas

Now that we’ve learned about the good parts of the arrow function and how it can help us write terser code and avoid some problems with the this keyword let’s take a look at its darker sides: when an arrow function doesn’t behave like a function.

ECMA-262 Function.prototype.bind and Arrow Functions

If Target is an arrow function or a bound function then the thisArg passed to this method will not be used by subsequent calls to the function

You cannot use bind with arrow functions. If you try to bind an arrow function to an object it won’t work. Let’s illustrate this with an example:

let saruman = {
  name: 'Saruman, the White',
  toString() {
    return this.name
  },
  raiseUrukhai() {
    console.log(`${this} raises a Urukhai from the pits of Isengard`)
    return { name: 'Uruk', hp: 500, strength: 18 }
  },
  telekineticStaffAttack: () =>
    console.log(`${this} uses his staff to throw 
you across the room "telekinetically" speaking`),
}

In this example we have saruman, another epic javascriptmancer, with a couple of methods. One raiseUrukhai is a regular function, the other telekineticStaffAttack uses an arrow function. If we call these methods using the dot notation:

saruman.raiseUrukhai();
// => Saruman, the White raises a Urukhai from the pits of Isengard

saruman.telekineticStaffAttack();
// => [object Window] uses his staff to throw
you across the room \"telekinetically\" speaking
// this would be undefined instead of Window if we used strict mode

If you look at the output of the telekineticStaffAttack method you may be confused to see the Window object as this instead of saruman himself. You have to remember that arrow functions have no this and so, when you use this inside an arrow function, you refer to this in the enclosing environment. In this case, because the arrow function is defined within an object in turn defined within the global scope, the closer this is the Window object (or undefined in case of strict mode). Again, the rules we learned about this in The Many a One JavaScript Quirks don’t apply to arrow functions and calling an arrow function using the dot notation doesn’t evaluate the arrow function in the context of the object.

If we try to use bind to bind these two methods to a different object:

// if we try to bind these two methods to a new object
let boromir = {
  name: 'Boromir of Gondor',
  toString() {
    return this.name
  },
}
let raiseUrukhaiBound = saruman.raiseUrukhai.bind(boromir)
raiseUrukhaiBound()
// => Boromir of Gondor raises a Urukhai from the pits of Isengard

We can appreciate how we can bind a normal function but when we try to bind an arrow function:

let telekineticStaffAttackBound = saruman.telekineticStaffAttack.bind(boromir);
telekineticStaffAttackBound();
// => undefined uses his staff to throw
you across the room \"telekinetically\" speaking
// didn't work, not telekinetic staff attack for Boromir

Nothing happens. Since an arrow function doesn’t have this it makes no sense to bind it.

Even though arrow functions are not the same as bound functions, once an arrow function is declared and encloses its nearest this it pretty much behaves in the same way as a bound function.

let Warg = function(name, size){
   this.name = name;
   this.size = size;
   this.name = '${name}, the ${size} warg`;
   // wargs don't bark, they wark
   this.wark = () => console.log(`${name} warks!: Wark! Wark!`);
   this.jump = (function(){
     console.log(`${name} jumps around`);
   }).bind(this);
}
let willyTheWarg = new Warg('willy', 'litte');

In this example I am using a constructor function and the new keyword to instantiate a fiery warg. We haven’t seen any of those concepts yet since I am reserving it for the OOP section of the series. They are still the best way to exemplify the similar behavior of arrow functions and bound functions so I hope you’ll forgive me. Essentially you use the new keyword to instantiate new objects via constructor functions. When you apply the new keyword on any function the JavaScript runtime instantiates an object {}, sets it as the this value of the function, then evaluates the function and finally returns it. This is useful because it is the this value that the wark method is going to enclose and safeguard for the rest of the program execution.

After creating willyTheWarg we got ourselves an arrow function wark and a bound function jump. If we execute any of them we will be able to appreciate how this refers to the warg itself:

// this is an arrow function
willyTheWarg.wark()
// => willy, the litte warg warks!: Wark! Wark!

// and this is the bound function
willyTheWarg.jump()
// => willy jumps around

This is the expected behavior, but what happens if we are mean and take these functions away from willyTheWarg?

Well the bound function, as we learned in JavaScript Quirks, will still have willyTheWarg as its context:

let jump = willyTheWarg.jump
jump()
// => willy, the litte warg warks!: Wark! Wark!

let goblin = { jump: jump }
goblin.jump()
// => willy, the litte warg warks!: Wark! Wark!

And the arrow function behaves in exact the same way, but instead of being explicitely bound to willyTheWarg it is implicitly bound by the closure over the this variable.

let wark = willyTheWarg.wark
wark()
// => willy, the litte warg warks!: Wark! Wark!

goblin.wark = wark
goblin.wark()
// => willy, the litte warg warks!: Wark! Wark!

This similar behavior and the fact that neither bound nor arrow functions can be bound (re-bound in the case of the bound function) makes both types of function practically identical in this context. The only different being that bound functions don’t need a closure, you can just bind a normal function to whatever object you want by just calling the bind method. Arrow functions on the other hand can be seen to be implicitly bound to their enclosing context by virtue of the closure.

ECMA-262 Function.prototype.apply, Function.prototype.call and Arrow Functions

If func is an arrow function or a bound function then the thisArg will be ignored by the function [[Call]] in step 6.

If func is an arrow function or a bound function then the thisArg will be ignored by the function [[Apply]] in step 5.

In addition to bind, you cannot use call nor apply on an arrow function to change its context. If you remember JavaScript Quirks, you can use call and apply to explicitly set the context in which a function is executed, that is, the value of this:

let caragor = {
  toString() {
    return 'scary caragor'
  },
}
let howl = function({ times }) {
  console.log(`${this} howls to the moon ${times} times!`)
}
// a normal function let's you set its context explicitly via apply or call
howl.apply(caragor, [{ times: 3 }])
// => scary caragor howls to the moon 3 times!
howl.call(caragor, { times: 4 })
// => scary caragor howls to the moon 4 times!

But if you try to use either apply or call with an arrow function, the context that you pass as argument will be completely ignored:

// an *arrow function* completely ignores the value of `this` passed as argument
willyTheWarg.wark.apply(caragor)
// => willy, the litte warg warks!: Wark! Wark!
willyTheWarg.wark.call(caragor)
// => willy, the litte warg warks!: Wark! Wark!

In the example above you can easily appreciate how instead of scary caragor the ${this} within the wark arrow function is evaluated as willy, the little. This demostrates how arrow functions ignore the context when called with either call or apply.

Arrow Functions And Arguments Gotchas

ECMA-262 FunctionDeclarationInstantiation

Arrow functions never have an arguments objects.

Another interesting feature of arrow functions is that they don’t have arguments object. What does that mean? Just like with this if you attempt to access the arguments object within an arrow function you’ll access the arguments of the enclosing environment.

If you remember More Useful Function Patterns - Multiple Arguments every function in JavaScript has a arguments object that you can use to access which arguments where passed to a function. So if you have a normal function that logs the arguments object:

function rememberWhatISaid() {
  console.log(`you said: ${Array.from(arguments).join(', ')}`)
}

You can demonstrate how the arguments object collects those arguments being passed to the function:

rememberWhatISaid('hello', 'you', 'there')
// => you said: hello, you, there
rememberWhatISaid('supercalifragilisticusespialidosus')
// => you said: supercalifragilisticusespialidosus

Not so with arrow functions:

let forgetWhatISaid = () => {
  console.log(`I am going to forget that you said: ${arguments}`)
}
forgetWhatISaid('I said Wazzaaaaa')
// => error ReferenceError: arguments is not defined

The arguments variable is not defined and thus we get a ReferenceError. Let’s define it and see what happens:

let arguments = ['trying something crazy']
let forgetWhatISaid = () => {
  console.log(`I am going to forget that you said: ${arguments}`)
}
forgetWhatISaid('I said Wazzaaaaa')
// => I am going to forget that you said: trying something crazy

We can also make the same experiment wrapping an arrow function inside another function:

function createMemoryWisp() {
  return () =>
    console.log(`*MemoryWisp*: You said... 
    ${Array.from(arguments).join(', ')}`)
}
let wispRememberThis = createMemoryWisp(1, 2, 3, 4, 'banana!')
wispRememberThis('important password', '123456789')
// => *MemoryWisp*: You said... 1, 2, 3, 4, banana!

So as you can see in both these examples, arrow functions don’t have their own arguments object and use the arguments object of their enclosing environment. But What if we want to send an arbitrary number or arguments to an arrow function? Well in that case you should use the rest operator:

function createMemoryWispWithRest() {
  return (...thingsToRemember) =>
    console.log(`*MemoryWisp*: You said... ${thingsToRemember.join(', ')}`)
}
let wispRememberThisAgain = createMemoryWispWithRest()
wispRememberThisAgain('important password', '123456789')
// => *MemoryWisp*: You said... important password, 123456789

In summary, whenever you use arguments inside an arrow function you’ll be accessing the enclosing environment’s arguments object. So if you want to send multiple arbitrary arguments to an arrow function use the rest operator.

Arrow Functions and the New and Super Operators

The new and super operators are two operators that we will see in depth in the OOP section of the series. The new operator lets you create new instances of objects when applied to a any function which will act as a constructor. The super keyword is new in ES6 and lets you access methods in parent objects within an inheritance chain.

In much the same way as with bind, call and apply, you cannot use new nor super with an arrow function.

Concluding

In this article you learned about arrow functions which resemble lambdas in C#. Arrow functions let you use a terser syntax than the normal function syntax and help you avoid problems with the this keyword by using the this value of their enclosing environment.

Arrow functions are a little bit special in what regards to this since they are the only functions in JavaScript that don’t have their own this value. Because of this characteristic you cannot use bind, call or apply to specify the context in which an arrow function will be evaluated. In a similar fashion you cannot use the new and super operators with an arrow function.

Additionally, arrow functions don’t have their own arguments object, if you try to access arguments inside an arrow function you’ll access the arguments object within the enclosing function. This means that if you want for an arrow function to take an arbitrary number of arguments you’ll need to use the rest syntax.

Interested in Learning More JavaScript? Buy the Book!

Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.

a JavaScriptmancy sample cover

Have a great week ahead!! :)

More Articles In This Series


Jaime González García

Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.Jaime González García