Introduction
1 . What is Default Binding (Standalone Function Call)?
2 . What is Implicit Binding (Method Call)?
4 . What is new Binding (Constructors)?
5 . What is Lexical this in Arrow Functions?
6 . What is Method Extraction and Lost this?
7 . What is this in Class Methods (ES6 Classes)?
8 . What is this in Event Handlers (DOM)?
9 . What is this in Timers and Callbacks?
10 . What is this with call/apply and Borrowing Methods?
11 . What is this in Prototypes and Methods Inherited from Prototype?
12 . What is this in Getters/Setters and Property Descriptors?
13 . What is this in Modules (ESM) and Top-Level?
14 . What is this and Arrow Functions in Methods (Pitfall)?
15 . What is this and Binding Performance/Memory Considerations?
16 . What is this and Closures (Combining Patterns)?
17 . What is this in call/apply for Partial Application?
18 . What is this and Debugging/Inspecting Context?
19 . What is this in Frameworks (React / Vue) - Practical Patterns?
20 . What is Best Practice: Avoiding this Ambiguity?
Conclusion
The JavaScript this keyword is powerful, and notoriously confusing. Unlike many languages where this is bound to an instance at definition time, JavaScript binds this at call-time (except in arrow functions). That makes this flexible, enabling method reuse and fluent APIs, but also error-prone when methods are detached, passed around as callbacks, or executed in strict mode.
If you master this, you’ll unlock:
Robust object APIs and fluent method chains
Correct behavior in callbacks, event handlers, and timers
Fewer bugs from lost context when passing functions around
A clear mental model for classes, prototypes, and arrow functions
Definition: Default binding occurs when a regular (non-arrow) function is invoked without an object base. this falls back to the global object in non-strict mode, and to undefined in strict mode.
Description:
This is the simplest call form: fn().
In browsers non-strict, this === window. In Node modules, the global differs; in strict mode this is undefined.
Default binding is the weakest rule; it is overridden by any other (implicit, explicit, new, lexical).
Key points:
a. Avoid relying on default binding for methods.
b. Strict mode is safer because it avoids accidental global access.
Code Example:
function defaultThis() {
'use strict';
console.log(this);
}
defaultThis(); // undefined (strict)
Definition: When a function is invoked as a property of an object (obj.method()), this is implicitly bound to that object — specifically the value to the left of the dot at call time.
Description:
Key points:
a. Method extraction can lose implicit binding.
b. The object used at call-site determines this, not where the function was defined.
Code Example:
const person = {
name: 'Alice',
say() { console.log(this.name); }
};
person.say(); // "Alice"
const fn = person.say;
fn(); // undefined or global name, implicit binding lost
3 . What is Explicit Binding (call, apply, bind)?
Definition: Explicit binding lets you set this manually using fn.call(thisArg, ...args), fn.apply(thisArg, argsArray), or fn.bind(thisArg) which returns a bound function.
Description:
.call invokes immediately with thisArg.
.apply is like call but takes an array.
.bind returns a new function permanently bound to thisArg.
Key points:
a. Useful to ensure correct this in callbacks.
b. .bind is common for event listeners and class methods.
Code Example:
function greet(g) { console.log(`${g}, I'm ${this.name}`); }
const user = { name: 'Tom' };
greet.call(user, 'Hello'); // "Hello, I'm Tom"
const bound = greet.bind(user, 'Hi');
bound(); // "Hi, I'm Tom"
Definition: When a function is called with new, JavaScript creates a new object and binds this to that new instance inside the constructor. The constructor implicitly returns the object (unless it returns another object).
Description:
this inside constructor refers to the new object.
Allows initialization of instance properties and prototypal inheritance.
Key points:
a. Forgetting new causes this to default bind (or undefined in strict), leading to bugs.
b. Prefer class syntax for clarity.
Code Example:
function Person(name) {
this.name = name;
}
const p = new Person('Mary');
console.log(p.name); // "Mary"
Definition: Arrow functions do not have their own this. They lexically capture this from the surrounding (enclosing) scope at definition time.
Description:
a. Use arrow functions to avoid .bind or closures for this.
b. Avoid arrow methods on objects if you need this to refer to the object itself.
Code Example:
const obj = {
id: 1,
regular() { console.log(this.id); },
arrow: () => console.log(this.id) // lexical `this` from outer scope
};
obj.regular(); // 1
obj.arrow(); // undefined (or global id) because `this` is not obj
Definition: Method extraction is taking a method off an object and calling it as a standalone function, causing implicit binding to be lost and this to change.
Description:
Happens when passing methods as callbacks or assigning to variables.
Solutions: .bind, arrow wrappers, or call inside object context.
Key points:
a. Always think how function will be invoked at runtime.
b. When passing methods to libraries, ensure this is preserved.
Code Example:
const counter = {
n: 0,
inc() { this.n++; console.log(this.n); }
};
setTimeout(counter.inc, 100); // `this` lost: NaN or error
setTimeout(counter.inc.bind(counter), 200); // works
Definition: Inside class instance methods (non-static), this refers to the instance when invoked as instance.method(); static methods receive the class as this if bound.
Description:
a. Use property initializer with arrow to auto-bind instance methods in React components.
b. Avoid mutating this outside constructor in unexpected ways.
Code Example:
class App {
constructor(name) { this.name = name; this.show = this.show.bind(this); }
show() { console.log(this.name); }
}
const a = new App('Demo');
const f = a.show;
f(); // "Demo" (bound)
Definition: In DOM event handlers registered via element.addEventListener, in a regular handler this is the element the handler is registered on; in arrow handlers, this is lexically inherited.
Description:
Native event handlers (like onclick) and addEventListener behave similarly for this.
With frameworks, behavior may vary.
Key points:
a. Use event.currentTarget for robust reference.
b. Be careful using arrow functions if you need the element as this.
Code Example:
button.addEventListener('click', function (e) {
console.log(this === e.currentTarget); // true
});
button.addEventListener('click', (e) => {
console.log(this); // lexical `this`, not the button
});
Definition: When functions are called by the runtime (e.g., setTimeout), they follow default binding unless explicitly bound.
Description:
setTimeout(fn, ...) executes fn as a standalone function: default binding applies.
Use .bind or arrow-wrapping to keep desired this.
Key points:
a. Beware of passing methods directly to timers.
b. A common pattern is setTimeout(() => obj.method(), 0) to preserve context.
Code Example:
const o = { x: 100, log() { console.log(this.x); } };
setTimeout(o.log, 10); // undefined
setTimeout(() => o.log(), 20); // 100
Definition: You can borrow methods from other objects by using call/apply to set this temporarily to a different object.
Description:
Useful to reuse Array methods on array-like objects.
call/apply let you control this for a single invocation.
Key points:
a. Array.prototype.slice.call(arguments) is a classic pattern.
b. ES6 spread [...] reduces need, but understanding borrowing remains useful.
Code Example:
function sum() { return Array.prototype.reduce.call(arguments, (a,b)=>a+b, 0); }
console.log(sum(1,2,3)); // 6
Definition: Prototype methods are invoked with this bound to the instance that called them (implicit binding), even if the method is defined on the prototype.
Description:
a. Avoid referencing prototype via this.constructor.prototype unless needed.
b. this remains dynamic even in prototype methods.
Code Example:
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { console.log(this.name + ' speaks'); };
const a = new Animal('Dog');
a.speak(); // "Dog speaks"
Definition: Within getter/setter functions, this refers to the object the property is accessed on (the receiver).
Description:
a. Use this to access backing fields.
b. Beware of binding getters to different receivers.
Code Example:
const o = {
_x: 1,
get x() { return this._x; },
set x(v) { this._x = v; }
};
console.log(o.x); // 1
Definition: In ES modules, top-level this is undefined (module scope), not the global object.
Description:
a. this inside functions in modules follows regular binding rules.
b. Use globalThis to access the global object explicitly.
Code Example:
// In an ES module
console.log(this); // undefined
Definition: Using arrow functions as methods causes this to refer to the enclosing lexical scope, not the object, often leading to unexpected behavior.
Description:
a. Do not define object methods as arrows if they need object this.
b. Use regular functions or class fields as arrow if you intentionally want lexical binding.
Code Example:
const obj = {
n: 5,
wrong: () => console.log(this.n),
right() { console.log(this.n); }
};
obj.wrong(); // undefined
obj.right(); // 5
Definition: Repeated .bind creates new bound functions, which can lead to more objects and possible memory usage; bound functions are separate from originals.
Description:
a. Use .bind judiciously in constructors.
b. For many instances, prefer prototype methods and avoid per-instance binding unless necessary.
Code Example:
class C {
constructor() {
this.handler = this.handler.bind(this); // bound per instance
}
handler() { console.log(this); }
}
Definition: Closures can capture this if used correctly: use arrow functions inside methods to retain outer this, or capture const self = this in older code.
Description:
a. Closures plus this create robust patterns for async callbacks.
b. Avoid var self = this if arrow functions are available.
Code Example:
class Timer {
constructor() { this.count = 0; }
start() {
setInterval(() => { this.count++; console.log(this.count); }, 1000);
}
}
new Timer().start();
Definition: You can pre-bind this and some arguments using Function.prototype.bind for partial application, with the returned function having this fixed.
Description:
bind is helpful for passing pre-configured methods as callbacks.
Bound functions ignore further .call binding; this remains fixed.
Key points:
a. You cannot re-bind a bound function's this with .call.
b. Useful in event-driven code.
Code Example:
function add(a,b){ return a+b + (this.offset||0); }
const bound = add.bind({offset:10}, 5);
console.log(bound(3)); // 18 (5+3 + 10)
Definition: Debugging this often requires checking call-site in stack traces or using tools to log this and understand binding.
Description:
Use console.log(this) and console.trace() to inspect.
Tools: DevTools, Node debugger, and Function.prototype.toString() sometimes help.
Key points:
a. When this is unexpected, inspect call chain.
b. Consider refactoring to avoid dynamic this if too tricky.
Code Example:
function f() { console.trace('trace this: ', this); }
const o = { f };
o.f(); // shows call stack and `this` as o
Definition: Frameworks manage this differently: React functional components avoid this, class components use this (binding), Vue proxies instance this to template and methods.
Description:
React functional approach discourages this. Hooks replace instance this.
Vue 2 used this in component methods; Vue 3 composition API reduces this reliance.
Key points:
a. In React class components, bind handlers in constructor or use arrow class fields.
b. Prefer functional patterns to avoid this bugs.
Code Example:
// React class example
class Button extends React.Component {
constructor(){ super(); this.onClick = this.onClick.bind(this); }
onClick() { console.log(this); }
render(){ return <button onClick={this.onClick}>Click</button>; }
}
Definition: Best practice is to prefer patterns that minimize confusing dynamic this: use modules, closures, pure functions, or explicit binding only when necessary.
Description:
Use arrow functions for callbacks, modules/top-level functions for utilities, and classes/patterns for instances.
Document when this is used and prefer explicit .bind or wrapper functions in critical code.
Key points:
a. Prefer function arguments over implicit this where possible.
b. Use strict mode, class, and modern syntax to reduce accidental global this.
Code Example:
// Prefer passing context explicitly
function process(ctx, value) { return ctx.multiply(value); }
const ctx = { multiply: v => v*2 };
console.log(process(ctx, 5)); // 10
this is dynamic, powerful, and a frequent source of confusion. The right mental model:
Decide how your function will be called (call-site), not where it is defined.
Use arrow functions to capture lexical this for callbacks.
Use .bind / .call / .apply to explicitly control this.
Use classes, modules, or pure functions to avoid dynamic context when clarity is paramount.
Use strict mode to avoid silent default bindings to the global object.
Mastering this turns surprising bugs into predictable code, improves API design, and simplifies debugging. Practice by reading call chains, intentionally choosing binding patterns, and refactoring code that relies on fragile default behavior.
Software Engineer
Senior Software Engineer