How Arrow Functions redefine this in JavaScript

How Arrow Functions redefine this in JavaScript

Learn how arrow functions simplify JavaScript by redefining this keyword

·

4 min read

Why do popular frameworks and packages like Mongoose, Vue JS, and others say not to use arrow functions (in some places)? We’ll learn about this in this post but first what is even an 🏹 arrow function?

Arrow Functions; a concise and “elegant” way of writing functions in modern JavaScript. Converting regular function to an arrow function:

// Regular function
function getFood(food, quantity) {
  return food.repeat(quantity);
}

// Arrow function
const getFood = (food, quantity) => {
  return food.repeat(quantity);
};

// Directly returning
const getFood = (food, quantity) => food.repeat(quantity);

💡 For returning something directly using the arrow function, wrap it with parentheses ().

const breakfast = () => ({
  food: "🥪",
  quantity: 3,
});

This conciseness is what makes arrow functions so 💪🏽 powerful.

Difference between Arrow Functions and Regular Functions

Arrow functions are more than just syntactic sugar for writing functions. There are some nuances that make it different from a regular function like:

  • It will implicitly return the value when you omit curly braces {}. For example: () => true
  • It's always an expression and thus it can’t be hoisted
  • 🔥 It doesn’t have its own this context as regular functions do.

The last point has some big implications. We will come back to this point but first, let’s look at the hoisting part.

Arrow Functions can’t be hoisted

Hoisting is the process where the interpreter moves the declaration of something to the top of their scope, prior to execution of the code. And because of this, you can something like this - run before wearing your 👟 shoes.

run();

function run() {
  console.log("🐢 Running...");
}

// Output:
// 🐢 Running...

But this is not the case with function expressions, they can’t be hoisted. Other things that can’t be hoisted are variables declared using let and const and class expressions. Since they are used before their definitions their value is set to undefined.

console.log(run); // undefined
run(); // TypeError: run is not a function

var run = function () {
  console.log("🐢 Running...");
};

// Output:
// undefined
// TypeError: run is not a function

https://media.giphy.com/media/Y3AQNvFtEM2rBNqPdv/giphy.gif

Understanding this context in Arrow Functions

this context in arrow functions refers to its outer scope i.e. where was defined, unlike regular functions where this context refers to the scope where it's being called.

class Food {
  constructor() {
    this.food = "🍞";
  }

  withArrowFunction() {
    setTimeout(() => {
      console.log(this.food);
    }, 1000);
  }

  withRegularFunction() {
    setTimeout(function () {
      console.log(this.food);
    }, 2000);
  }
}

const food = new Food();
food.withArrowFunction(); // 🍞, since the function is defined in the Animal class
food.withRegularFunction(); // undefined, since the funciton is called in the global scope

So arrow functions remove this weird behavior of regular functions with respect to this context. But there is more to this. When working with packages like Mongoose OR Vue JS there are some places where you’ve to use only regular functions and 🚫 not arrow functions.

Mongoose_avoid_using_arrow_function.png

Vue_JS_avoid_using_arrow_function.png

So read the documentation for such things

A side history of problems of this context in regular functions was handled prior to the dawn of arrow functions. The following example displays the limitations of this context while using regular functions. A way around this limitation was to use a variable (mostly named self) and assign its value to this context thus you can use the self variable consistently.

function getFood() {
  let self = this;
  this.food = "🍕 Pizza";

  // Regular function in the callback
  setTimeout(function () {
    console.log(this.food); // undefined
    console.log(self.food); // 🍕 Pizza
  }, 1000);

  // Arrow function in the callback
  setTimeout(() => {
    console.log(this.food); // 🍕 Pizza
  }, 2000);
}

getFood();

// Output:
// undefined
// 🍕 Pizza
// 🍕 Pizza

Anatomy of the code snippet:

  • 1️⃣ Inside the first setTimeout, this refers to the scope where the callback is going to be called i.e. the setTimeout scope, where the food property isn’t defined, and because of this, we get undefined.
  • 2️⃣ A hack was to use the concept of closures. A variable, usually named self, is defined in the scope to which you want this context to refer. The value of this variable is the this context. Now JavaScript will close over the variable self and you can use it consistently to refer to this context.
  • 3️⃣ Now, this is a dirty method and is removed by the use of the arrow function. In the arrow function, this context refers to the scope where the arrow function is defined.

Conclusion

Arrow functions are concise and do simplifies your code and make it easy to debug. Working with this context is also simple. Hope this was helpful to you. If you liked this post then do 👍🏼 like and give your 🎙 feedback. ✅ Follow to stay up to date with my new posts.

https://media.giphy.com/media/U7IaMMFoVwo7u/giphy.gif