`this`, call, apply & bind
How `this` is decided, why arrow functions differ, and controlling it with call, apply and bind.
this is one of the most confusing parts of JavaScript, mostly because it doesn't work like in Java or Python. The good news: there are only a handful of rules. Learn them, and you'll understand bugs that puzzle many developers — and the call, apply and bind methods that control this.
Run these examples with Node as ES modules (save as
.mjs) or in the browser console. Modules use strict mode, which affects one rule below.
Rule 1: method call → this is the object before the dot#
When you call user.greet(), this is user. The same function attached to a different object gets a different this:
this is decided at call time, by how the function is called — not where it was defined.
Rule 2: plain function call → this is undefined (strict mode)#
In strict mode (all modules and classes), a plain call gives this === undefined. In old non-strict scripts it's the global object (window / globalThis) — which silently hides bugs.
The lost-this bug
This happens whenever you pass a method as a callback: setTimeout(counter.increment, 100), button.addEventListener("click", counter.increment), array.map(obj.method). The function gets called later without its object.
Rule 3: arrow functions don't have their own this#
Arrow functions take this from the scope around them (lexical this), and it can't be changed:
With a regular function () {} callback, this would be undefined inside it. Before arrows, people wrote const self = this; to work around this.
The flip side: don't use arrows as object methods if you need this:
Rule 4: new → this is the brand-new object#
Classes work the same way — more in the next lesson.
Rule 5: call, apply and bind set this explicitly#
call — call now, arguments listed
apply — call now, arguments as an array
apply was popular for passing arrays as arguments (Math.max.apply(null, nums)). Today, spread does that: describe.call(person, ...args) or Math.max(...nums).
bind — return a new function with this locked in
bind can also pre-fill arguments (partial application):
A bound function's this can't be changed again — not even by another bind or call.
this in event listeners#
In a DOM listener written as a regular function, this is the element the listener is attached to (same as event.currentTarget):
Using event.currentTarget is clearer and works with both styles.
this in classes#
Class methods have the same lost-this problem when passed as callbacks. A common fix is an arrow function as a class field, which captures the instance:
The decision checklist#
When you see this, ask in order:
- Is it inside an arrow function? → Use
thisfrom the enclosing scope. - Was the function called with
new? → The new object. - Was it called with
call/apply, or is it bound? → The given object. - Was it called as
obj.method()? →obj. - Otherwise →
undefined(strict) or the global object (sloppy scripts).
Common mistakes#
- Passing
obj.methodas a callback and losingthis. Use() => obj.method()orobj.method.bind(obj). - Writing object methods as arrow functions.
- Expecting
thisinside a nested regular function to match the outer method. - Using
bindinside a render loop or a frequently-called function — it creates a new function every time.
What's next#
Now that this makes sense, you're ready for prototypes, classes and inheritance — how JavaScript shares behaviour between objects.
Check your understanding
Quick quiz
1.What decides the value of
thisinside a regular (non-arrow) function?2.What is the difference between
callandapply?3.
const f = user.greet; f();— why doesthis.namefail insidegreet(in strict mode)?
Finished reading?
Mark this lesson complete to track your progress.