this, call, apply & bind

this is a special keyword that refers to the object a function is working with. Unlike ordinary variables, its value isn't fixed where the function is written — it's decided by how the function is called. Understanding a handful of rules removes almost all the confusion.

Rule 1: method calls

When a function is called as object.method(), this is the object before the dot:

JS method.js
const user = {
  name: "Ana",
  greet() {
    return `Hi, I'm ${this.name}`;
  },
};

const robot = { name: "R2", greet: user.greet };

console.log(user.greet());
console.log(robot.greet());
Output
Hi, I'm Ana
Hi, I'm R2

It's the same function both times. What changes is the object to the left of the dot at the moment of the call.

Rule 2: plain function calls

Call a function on its own — not as a method — and this is undefined in strict mode (modules, classes, and any file with "use strict"). In old sloppy-mode scripts it's the global object instead. This is how methods "lose" their this:

JS lost.js
"use strict";
const user = {
  name: "Ana",
  greet() {
    return `Hi, I'm ${this.name}`;
  },
};

const greetFn = user.greet;
console.log(greetFn());
Output
Uncaught TypeError: Cannot read properties of undefined (reading 'name')

Assigning user.greet to a variable copies just the function. Called without a dot, it has no object. The same thing happens when you pass a method as a callback, like setTimeout(user.greet, 100).

Rule 3: call and apply

call and apply run a function immediately with an explicit this. They differ only in how extra arguments are passed:

JS call-apply.js
function introduce(greeting, punctuation) {
  return `${greeting}, I'm ${this.name}${punctuation}`;
}

const ana = { name: "Ana" };

console.log(introduce.call(ana, "Hello", "!"));
console.log(introduce.apply(ana, ["Hey", "?"]));
Output
Hello, I'm Ana!
Hey, I'm Ana?

Mnemonic: apply takes an array. With spread syntax, call covers both cases, so apply is rarely needed today.

Rule 4: bind

bind doesn't call the function — it returns a new function with this permanently fixed:

JS bind.js
const user = {
  name: "Ana",
  greet() {
    return `Hi, I'm ${this.name}`;
  },
};

const bound = user.greet.bind(user);
const callLater = fn => fn();

console.log(callLater(bound));
Output
Hi, I'm Ana

Bound functions are the classic fix for passing methods as callbacks. bind can also pre-fill arguments: const addTen = add.bind(null, 10).

Rule 5: arrow functions inherit this

Arrow functions ignore all of the above. They don't have their own this; they use the one from the surrounding code. That makes them the easiest fix for callbacks inside methods:

JS arrow.js
const clock = {
  ticks: 0,
  run() {
    [1, 2, 3].forEach(() => this.ticks++);
    return this.ticks;
  },
};

console.log(clock.run());
Output
3

A regular function () { this.ticks++ } callback would have its own this (undefined), and the code would crash.

Rule 6: constructors and classes

When a function is called with new — including class constructors — this is the brand-new object being created (lessons 38–39).

this in event handlers

In a regular-function event listener, this is the element the listener is attached to. In an arrow function, it's whatever surrounded the arrow. Using event.currentTarget instead avoids the question entirely:

Try it yourself
Result

Summary

How the function is calledthis is…
obj.method()obj
fn()undefined (strict) / global object (sloppy)
fn.call(x), fn.apply(x)x
fn.bind(x) then calledx, always
new Fn()The new object
Arrow functionWhatever this was where the arrow was written

Try it yourself

Try it yourself
Console output