Beware of JavaScript arrow functions

Arrow functions are a fantastic addition to JavaScript. However, I've noted that many devs use arrow functions only. They seem to think that it's "the new way" to create functions, while using the function keyword is "the old way".

But that's not correct, arrow functions aren't 100% equivalent to classic functions. There are subtle but important differences that can cause big headaches if you aren't aware of them.

In classic functions, the value of the this keyword is dynamic, because it depends on how you call them.

If you call a function as the method of an object, this refers to that object. But if you call a function as a regular function (i.e. not as an object's method), this represents the global object (or undefined in strict mode.)

myObject.myMethod(); // this == myObject
myFunction(); // this == global object or undefined

With classic functions, you call them using the new keyword. In this case, you'll create an object and this will refer to that new object.

// this == the new object
const myObject = new ConstructorFunction();

Also, you can manually set the value of this using the .bind method (functions in JavaScript are objects). It doesn't change the original function, but it returns a new version with the new value for this.

const boundFunction = normalFunction.bind({name: "Bob", age: 40});
normalFunction(); // this == global object or undefined
boundFunction(); // this == {name: "Bob", age: 40}

You can't do anything of this with arrow functions.

In arrow functions, it's often said that they "have no this". The value of this in an arrow function is "inherited" from the context where the function was created.

It means, if you create an arrow function in the global context (i.e. not inside an object or a function), this refers to the global object or undefined in strict mode. If you declare an arrow function inside a class, this is the instance of the class.

And if you declare an arrow function inside another function, it will have the same this as the outer function.

// Declaring an arrow function in the global context
// this == global object or undefined
const sayHello1 = () => "Hello";

class MyClass {
  // Declaring an arrow function in a class
  // this == object
  sayHello2 = () => "Hello";
}

function outerFunction() {
  
  // Declaring arrow function inside
  // another function
  // this == the same value as in outerFunction
  const innerFunction = () => "Hello"
}

And the most important thing, you can't change the value of this in an arrow function. Even .bind fails silently! Calling this method won't throw an error, but it will return a new version of the function... with the same this as the original.

// Declaring an arrow function in the global context
// this == global object or undefined
const sayHello = () => "Hello";
sayHello(); // this == global object or undefined

const sayHello2 = sayHello.bind({ name: "Alice", age: 30 });
sayHello2();  // this == global object or undefined

Another difference occurs when declaring methods inside a class.

If you declare class methods the "standard" way, the same function will be reused among all instances of that class. But if you use arrow functions, every time you create a new instance, a new copy of the function will be created for that instance.

It's important to say it again, arrow functions are a fantastic addition to JavaScript. If the function body is a simple expression or if you don't want to deal with the this keyword, they are very useful. You just need to know when to use them.

Become a Better JavaScript Developer

Easy, actionable steps to level up your JavaScript skills, right to your inbox. Enter your name and Email address below to subscribe: