Skip to content

this and Context

this is a special keyword that refers to the execution context — the object that is considered the receiver of the current function call. Unlike most languages, this in JavaScript is not bound at definition time; it is determined at call time by how the function is invoked.

// 1. Method call — this is the object before the dot
const user = {
name: 'Alice',
greet: function() {
return 'Hi, I am ' + this.name;
},
};
console.log(user.greet()); // 'Hi, I am Alice'
// 2. Plain function call — this is undefined (strict mode) or globalThis
function whoAmI() {
return typeof this; // 'undefined' in strict mode
}
// 3. Constructor call — this is the newly created object
function Person(name) {
this.name = name;
}
const p = new Person('Bob');
console.log(p.name); // 'Bob'

The most common pitfall: extracting a method from an object loses the binding.

const timer = {
label: 'tick',
start: function() {
// After 0 ms, fn is called as a plain function — this.label is undefined
setTimeout(function() {
console.log(this.label); // undefined — 'this' is not timer
}, 0);
},
};

These three methods let you explicitly set this.

function introduce(greeting, punctuation) {
return greeting + ', I am ' + this.name + punctuation;
}
const obj = { name: 'Carol' };
// call — pass arguments individually
introduce.call(obj, 'Hello', '!'); // 'Hello, I am Carol!'
// apply — pass arguments as an array
introduce.apply(obj, ['Hey', '.']); // 'Hey, I am Carol.'
// bind — returns a NEW function with this permanently bound
const boundIntroduce = introduce.bind(obj);
boundIntroduce('Hi', '?'); // 'Hi, I am Carol?'
JavaScript

Arrow functions do not have their own this. They inherit this from the lexical enclosing scope — the surrounding regular function or module scope at the point where they are written.

const timer2 = {
label: 'tick',
start: function() {
// Arrow function inherits 'this' from start() — which is timer2
setTimeout(() => {
console.log(this.label); // 'tick' — correctly bound
}, 0);
},
};

This makes arrow functions the natural choice for callbacks inside methods.

A plain <script> runs in sloppy mode by default, where a detached this call silently falls back to the global object instead of throwing — the demo below opts into strict mode explicitly so the detached call fails the way it would inside an ES module or a class, both of which are strict mode automatically.

JavaScript

Six call shapes cover essentially every this bug you will meet in real code:

Call shapeWhat this isLoses binding?
obj.method()obj — the object before the dotNo
const fn = obj.method; fn() (detached)undefined in strict mode (throws on access); the global object in sloppy modeYes
Arrow function nested inside a methodWhatever this was in the enclosing method — inherited, not its ownNo
Class field arrow (prop = () => { ... })The instance, permanently — bound once at constructionNo
Regular function passed to setTimeout/array callbackNot the object you expected — a timer object or undefined, never the object it was “on”Yes
fn.bind(obj) / .call(obj) / .apply(obj)Explicitly obj, forced regardless of how the result is later calledNo (that is the point)
flowchart TD
  A["How is the function called?"] --> B["obj.method() -- this = obj"]
  A --> C["detached: const fn = obj.method; fn() -- this LOST"]
  A --> D["arrow nested in a method -- this inherited from enclosing scope"]
  A --> E["class field arrow -- this = instance, bound at construction"]
  A --> F["passed to setTimeout / array callback as a regular function -- this LOST"]
  A --> G["fn.bind(obj) / .call(obj) / .apply(obj) -- this forced to obj"]
Which call shapes keep this, and which lose it

The demo above already covers the method call and the detached call. This one covers the other three shapes that most often surprise a reviewer: an arrow nested in a method, a class field arrow, and a regular function handed to setTimeout.

Node.js
Output (verified on Node 24)
detached class-field arrow still works: 1
nested arrow this.label: inherited-arrow
regular setTimeout callback this.label: undefined
arrow setTimeout callback this.label: tick

Notice the ordering in the output: the two setTimeout callbacks always run after the synchronous code (including the class-field arrow log) has finished — this is the event loop deferring their macrotask callbacks, covered in depth in the Event Loop & Async module.

Think of this as an invisible extra parameter that JavaScript fills in at the call site, not at the definition site — like an unlabeled first argument every non-arrow function secretly receives. obj.method() fills it with obj. A bare fn() fills it with undefined (strict) or the global object (sloppy). fn.call(obj)/fn.apply(obj)/fn.bind(obj) let you fill it in yourself. Arrow functions are the one exception: they never receive this invisible parameter at all — an arrow function textually reads whatever this was already in scope where it was written, exactly like any other variable it closes over.

ChoiceBenefitCost
Regular function as a methodGets its own dynamic this, reusable across different receiver objects (fn.call(otherObj))Easy to lose this by extracting or passing the function as a callback
Arrow function as a callbackCannot lose this — it was never its own to loseCannot be re-targeted with call/apply/bind; unsuitable as an object method itself
Class field arrowAuto-bound once, safe to pass as an event handler or callback directlyOne function instance is created per object instance, not shared on the prototype (a small memory cost for many instances)
.bind(obj)Makes the correct this permanent and impossible to override, even accidentallyCreates a new function object on every call — avoid calling .bind() inside a hot loop
  • “Arrow functions are just shorter syntax for regular functions.” They also change semantics: no own this, no own arguments, and they cannot be used as constructors (new arrowFn() throws).
  • “If a function is defined inside an object literal, this always refers to that object.” Only true for a function written with method() {} or method: function() {} shorthand and called as obj.method(). The same function detached and called alone loses that binding entirely.
  • “bind runs the function immediately.” fn.bind(obj) returns a brand-new function without calling fn; you must still invoke the result.
  • “An arrow class field costs nothing compared to a prototype method.” It is bound once per instance, which is exactly what you want for a callback — but it does mean N instances create N separate function objects, unlike a prototype method which is shared by every instance.

The most common this bug in AI-generated code is passing an object method directly as a callback — setTimeout(obj.method, 1000), array.map(obj.method), emitter.on('event', obj.method) — all of which detach the method from obj at the moment they are passed, exactly like the classic detached-call bug, just one level of indirection away from being obvious.

🐛 Spot the bug

class Poller {
constructor(url) {
this.url = url;
this.attempts = 0;
}
poll() {
this.attempts += 1;
console.log('polling', this.url, 'attempt', this.attempts);
}
start() {
setInterval(this.poll, 1000); // looks fine — poll is "the method"
}
}
new Poller('https://example.com').start();
Show the answer

setInterval(this.poll, 1000) passes the poll function by itself, detached from the Poller instance — exactly the same bug as const fn = obj.method; fn(). When the timer fires, poll runs with this as undefined (class bodies are strict), so this.attempts += 1 throws TypeError: Cannot read properties of undefined. The fix is to preserve the binding explicitly: setInterval(this.poll.bind(this), 1000), setInterval(() => this.poll(), 1000), or declare poll as a class field arrow (poll = () => { ... }) so it is always bound to the instance, however it is passed around.

When is this determined for a regular (non-arrow) JavaScript function?
What does fn.bind(obj) return?
Why do arrow functions work well as callbacks inside class methods?
What happens when the timer fires?
class Poller {
  constructor() { this.count = 0; }
  poll() { this.count += 1; return this.count; }
  start() { return setTimeout(this.poll, 0); }
}
const p = new Poller();
p.start();
What does this print, and why does it differ from a normal detached method?
class Counter {
  count = 0;
  inc = () => { this.count += 1; return this.count; };
}
const c = new Counter();
const detached = c.inc;
console.log(detached());