This keyword with Arrow Functions => Simple & Code Examples

When using traditional function syntax, the value of "this" inside the function is determined by how the function is called, such as through an object method invocation or as a standalone function.

However, when using arrow functions, the value of "this" is determined lexically, based on the surrounding context in which the arrow function is defined. This means that the arrow function captures the value of "this" from its enclosing scope, rather than from the function or method in which it is called.

Example in JavaScript:

const person = {
  name: 'Esther',
  greet: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is Esther."

const greetFunction = person.greet;
greetFunction(); // Output: "Hello, my name is undefined."

const arrowFunction = () => {
  console.log(`Hello, my name is ${this.name}.`);
};

arrowFunction(); // Output: "Hello, my name is undefined."

The arrow function is defined outside of any function or object, so it has no surrounding context to capture this from. In strict mode, the value of this inside the arrow function will be undefined. In non-strict mode, the value of this inside the arrow function will be the global object (window in a browser, or global in Node.js).

Here's the same example in TypeScript:

interface Person {
  name: string;
  greet: () => void;
}

const person: Person = {
  name: 'Esther',
  greet: function(this: Person) {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is Esther."

const greetFunction: () => void = person.greet;
greetFunction(); // Output: "Hello, my name is undefined."

const arrowFunction: () => void = () => {
  console.log(`Hello, my name is ${this.name}.`);
};

arrowFunction(); // Output: "Hello, my name is undefined."

In this example, the arrow function is also defined outside of any function or object, and there is no type annotation to specify the expected this type. When calling the arrow function, the value of this is not specified, so it defaults to undefined.

In both JS and TS code examples, the output of the arrow function is undefined because arrow functions capture this from their surrounding context, rather than having their own this binding like traditional functions.

What can be done

To fix the issue and have the arrow function output the correct value, you can use a closure to capture this from the surrounding context and pass it as an argument to the arrow function, like so:

JavaScript:

const person = {
  name: 'Esther',
  greet: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is Esther."

const greetFunction = person.greet;
greetFunction(); // Output: "Hello, my name is undefined."

const arrowFunction = (self) => {
  console.log(`Hello, my name is ${self.name}.`);
};

arrowFunction(person); // Output: "Hello, my name is Esther."

TypeScript

interface Person {
  name: string;
  greet: () => void;
}

const person: Person = {
  name: 'Esther',
  greet: function(this: Person) {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

person.greet(); // Output: "Hello, my name is Esther."

const greetFunction: () => void = person.greet;
greetFunction(); // Output: "Hello, my name is undefined."

const arrowFunction: (self: Person) => void = (self) => {
  console.log(`Hello, my name is ${self.name}.`);
};

arrowFunction(person); // Output: "Hello, my name is Esther."

In this example, we define the arrow function to take an argument named self, which is used inside the function to access the name property. When we call the arrow function and pass the person object as the self argument, the arrow function can correctly output the value of this.name.


In JavaScript and TypeScript, arrow functions do not have their own this binding, so they capture this from their surrounding context. When an arrow function is defined outside of any function or object, its surrounding context is the global object, which can lead to unexpected behavior.

To work around this issue, you can use a closure to capture this from the surrounding context and pass it as an argument to the arrow function. This is commonly done by defining a variable named self or that to capture this inside a traditional function, and then passing self or that as an argument to an arrow function that needs to access this.

By using a closure and passing this as an argument, you can ensure that arrow functions have access to the correct this value and avoid unexpected behavior. However, this approach can result in more verbose code, so it's generally only used when necessary.

0
Subscribe to my newsletter

Read articles from {{ MonaCodeLisa }} directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

{{ MonaCodeLisa }}
{{ MonaCodeLisa }}

Hello, I'm Esther White, I am an experienced FullStack Web Developer with a focus on Angular & NodeJS.