The this Keyword
Decided by How You Call It
October 21, 2026 • 2 min read
🤖 AI Agent experiment: since 2026 this site is written, reviewed, tested and shipped by orchestrated AI agents, with a human approving every change.
this does not point to where a function is written, it points to however that function got called. That single rule explains most of the confusion around it, including the classic case where a method works fine attached to an object and then breaks the moment you pass it around as a callback.
We will look at how the call site decides this, see it break in a common callback situation, and then fix it with call, apply and bind.
How it Works
Call a function as a method on an object and this inside that function is the object before the dot.
const user = {
name: "Mario",
greet() {
return `Hi, I am ${this.name}`
},
}
console.log(user.greet()) // Hi, I am MarioNow take that same greet function and call it on its own, detached from user, and this is gone.
const detached = user.greet
console.log(detached()) // Hi, I am undefineddetached is still the exact same function, nothing about its code changed. What changed is the call site, detached() has no object before the dot, so this falls back to undefined in strict mode, or to the global object outside of it.
Fixing it with call and apply
call and apply let you choose this yourself when you invoke the function. They do the same thing, call takes the arguments one by one and apply takes them as an array.
function greet(greeting) {
return `${greeting}, I am ${this.name}`
}
const mario = { name: "Mario" }
console.log(greet.call(mario, "Hi")) // Hi, I am Mario
console.log(greet.apply(mario, ["Hi"])) // Hi, I am MarioBoth calls run greet with this set to mario, even though greet was never attached to that object in the first place.
Locking it with bind
call and apply run the function right away. bind instead returns a new function with this permanently attached, which is exactly what you need for a callback that gets invoked later by something else, like a timer or an event listener.
const button = {
label: "Save",
onClick() {
console.log(`${this.label} clicked`)
},
}
const handler = button.onClick.bind(button)
setTimeout(handler, 0) // Save clickedWithout bind, setTimeout would call handler with nothing before it, the same way the detached greet from earlier lost its object, and this.label would come back undefined.
Conclusion
this stops being a mystery once you stop asking what the function is and start asking how it got called. A method attached to an object has one, the same function handed off as a plain reference does not, and call, apply and bind are the three tools for choosing it on purpose instead of hoping the call site gets it right. Arrow functions sidestep the whole problem by capturing this from the surrounding scope, which is worth a post of its own. If you want another case of a value getting captured at the wrong moment, I wrote about that in this post about closures.
Happy binding!