Back to roadmaps vanilla-js Course

Arrow Functions & Lexical This

Introduced in ES6, arrow functions offer a concise syntax for writing function expressions and solve historical confusion surrounding the this keyword.

1. Arrow Function Syntax

Arrow functions are defined using the "fat arrow" operator (=>):

// Regular Function Expression
const square = function(x) {
  return x * x;
};

// Arrow Function
const squareArrow = (x) => {
  return x * x;
};

Syntax Simplifications:

  1. Single Parameter: You can omit the parentheses around the parameter list.
const double = x => { return x * 2; };
  1. Implicit Return: If the function body contains only a single expression, you can omit the curly braces {} and the return keyword.
const multiply = (a, b) => a * b; // Implicitly returns a * b
  1. Returning Objects: To implicitly return an object literal, wrap it in parentheses to prevent the engine from misinterpreting it as function brackets:
const getUser = id => ({ id: id, role: "Guest" });

2. Dynamic vs. Lexical this

The biggest functional difference between regular functions and arrow functions is how they handle the this keyword.

A. Regular Functions: Dynamic this

In a regular function, this is dynamic. Its value depends entirely on how the function is called:

  • Called as a method of an object: this points to that object.
  • Called as a standalone function: this points to window (or undefined in strict mode).
const user = {
  name: "Alice",
  greet: function() {
    console.log(`Hello, I am ${this.name}`);

    // Callback function inside:
    setTimeout(function() {
      // Called standalone by timer, this points to window!
      console.log(`My name is still ${this.name}`);
    }, 100);
  }
};
user.greet();
// Output:
// "Hello, I am Alice"
// "My name is still undefined" (window.name is undefined)

B. Arrow Functions: Lexical this

Arrow functions do not have their own this binding. Instead, they inherit this lexically from their enclosing scope.

const userCorrected = {
  name: "Alice",
  greet: function() {
    console.log(`Hello, I am ${this.name}`);

    // Arrow function inherits 'this' from greet():
    setTimeout(() => {
      console.log(`My name is still ${this.name}`);
    }, 100);
  }
};
userCorrected.greet();
// Output:
// "Hello, I am Alice"
// "My name is still Alice"

3. When NOT to Use Arrow Functions

Because arrow functions do not bind their own this, you should avoid using them in:

  1. Object Methods: If you use an arrow function as an object method, this will point to the outer scope (e.g. window), not the object itself.
  2. Event Listeners: In DOM event handlers, a regular function binds this to the element that triggered the event. An arrow function will bind it to the surrounding lexical scope (typically window).
const btn = document.querySelector('button');
// AVOID:
btn.addEventListener('click', () => {
  this.classList.add('active'); // Error: cannot read property of undefined
});

In the next guide, we will explore variables scopes, context hoisting, and closures.

Published on Last updated: