The this keyword

Learn the small set of rules that decide what this refers to, and fix the lost this that breaks callbacks.

  • Beginner
  • 7 min read
  • Updated 17 Oct 2026

What you will learn

  • Predict the value of this
  • Fix lost this in callbacks
  • Use call, apply and bind deliberately

Before you start

this is a special variable that refers to the object a function is working on. Unlike ordinary variables, it is not fixed where the function is written. For regular functions it is decided each time the function is called. Learn the four rules below and this stops being mysterious.

Rule 1: called as a method, this is the object before the dot

JavaScript
const course = {
  title: 'JavaScript Complete Course',
  describe() {
    return `This is the ${this.title}`;
  },
};

console.log(course.describe());
// → This is the JavaScript Complete Course

course.describe() has course before the dot, so inside describe, this is course. The same function can serve many objects:

JavaScript
function describe() {
  return `Title: ${this.title}`;
}

const a = { title: 'HTML', describe };
const b = { title: 'CSS', describe };

console.log(a.describe()); // → Title: HTML
console.log(b.describe()); // → Title: CSS

Rule 2: called on its own, this is undefined

Call a regular function without an object in front of it and this is undefined in strict mode. ES modules and classes are always strict, and you should assume strict mode everywhere.

JavaScript
'use strict';

function whoAmI() {
  return this;
}

console.log(whoAmI()); // → undefined

In old non-strict scripts, this falls back to the global object (window in browsers). That hides bugs, which is one reason strict mode exists.

The lost this

Rule 2 causes the most common this bug. Take a method off its object, and the object is no longer before the dot when it is called:

JavaScript
'use strict';

const timer = {
  seconds: 0,
  tick() {
    this.seconds += 1;
    console.log(this.seconds);
  },
};

timer.tick(); // → 1

const tick = timer.tick;
tick();
// TypeError: Cannot read properties of undefined (reading 'seconds')

You rarely write const tick = timer.tick deliberately. It happens implicitly whenever you pass a method as a callback:

JavaScript
setTimeout(timer.tick, 1000);
// The timer calls tick() on its own, so this is not timer.

setTimeout receives the function only. It has no idea that the function came from timer. (In browsers, timers call the callback with this set to window, so you get NaN instead of an error, which is even harder to spot.)

There are three standard fixes.

Fix 1: wrap it in an arrow function

JavaScript
setTimeout(() => timer.tick(), 1000);

The arrow function calls timer.tick() with timer before the dot, so rule 1 applies. This is the most readable fix and the one to reach for first.

Fix 2: bind it

bind returns a new function with this permanently set:

JavaScript
const boundTick = timer.tick.bind(timer);
setTimeout(boundTick, 1000);

Binding is useful when you need to keep a reference to the same function, for example to remove an event listener later.

Fix 3: use an arrow function inside the method

See rule 4 below.

Rule 3: call and apply set this explicitly

call and apply invoke a function immediately with the this you choose. They differ only in how they take arguments:

JavaScript
function introduce(greeting, punctuation) {
  return `${greeting}, I am ${this.name}${punctuation}`;
}

const person = { name: 'Priya' };

console.log(introduce.call(person, 'Hello', '!'));
// → Hello, I am Priya!
console.log(introduce.apply(person, ['Hi', '.']));
// → Hi, I am Priya.
MethodRuns the function?Arguments
fn.call(obj, a, b)Yes, nowListed one by one
fn.apply(obj, [a, b])Yes, nowOne array
fn.bind(obj, a)No, returns a new functionOptional, fixed in front

With spread syntax (lesson 23) you can write fn.call(obj, ...args), so apply is rarely needed in new code.

Calling a function with new is a fourth way to set this: it becomes the newly created object. Classes in lesson 36 build on this.

Rule 4: arrow functions do not have their own this

An arrow function ignores all the rules above. It uses the this of the scope it was written in, exactly like an ordinary variable from an outer scope. That makes arrows ideal for callbacks inside methods:

JavaScript
const quiz = {
  title: 'Capitals',
  questions: ['France', 'Japan'],
  list() {
    this.questions.forEach((country) => {
      console.log(`${this.title}: ${country}`);
    });
  },
};

quiz.list();
// → Capitals: France
// → Capitals: Japan

The arrow callback borrows this from list, which was called as quiz.list(). With a regular function callback, this inside it would be undefined and the code would throw.

The same feature makes arrows the wrong choice for methods:

JavaScript
const broken = {
  title: 'Capitals',
  describe: () => `Quiz: ${this.title}`,
};

console.log(broken.describe());
// In a module: TypeError, because this is undefined at the top level

The arrow was written at the top level of the file, so it takes the top level this, not the object. Use method shorthand (describe() { }) for methods and arrows for callbacks inside them. bind, call and apply cannot change an arrow function's this either.

this in event listeners

When the browser calls a regular function as an event listener, this is the element the listener is attached to, the same as event.currentTarget:

JavaScript
const button = document.querySelector('#save');

button.addEventListener('click', function () {
  console.log(this === button); // → true
});

With an arrow function, this would come from the surrounding code instead. Using event.currentTarget makes the intent clear whichever kind of function you write, so prefer it:

JavaScript
button.addEventListener('click', (event) => {
  event.currentTarget.disabled = true;
});

A quick decision list

To find this inside a function, ask these questions in order:

  1. Is it an arrow function? Then this is whatever it is in the surrounding code.
  2. Was it called with new? Then this is the new object.
  3. Was it called with call or apply, or created with bind? Then this is the object passed in.
  4. Was it called as something.method()? Then this is something.
  5. Otherwise, this is undefined in strict mode.

Summary

  • For regular functions, this is decided by how the function is called, not where it is written.
  • obj.method() sets this to obj; a plain call leaves it undefined in strict mode.
  • Passing a method as a callback loses this. Fix it with an arrow wrapper or bind.
  • call and apply run a function with a chosen this; bind returns a new bound function.
  • Arrow functions take this from their surroundings: use them for callbacks, not for methods.

Further reading