Q19 of 40 · JavaScript
How does the `this` keyword work across different JavaScript contexts?
Short answer
Short answer: `this` is determined at call time, not definition time (except arrow functions). In a method call `this` is the object; in a constructor it's the new instance; in a bare call (strict mode) it's `undefined`. Arrow functions inherit `this` from their enclosing lexical scope.
Detail
this is one of JavaScript's most confusing features because its value depends on how a function is called, not where it is defined.
Implicit binding (method call): obj.method() — this is obj. Extracting the method loses the binding: const fn = obj.method; fn(); makes this undefined in strict mode.
Explicit binding: call(), apply(), and bind() set this explicitly.
new binding (constructor): new Fn() creates a new object, sets it as this, runs the constructor, and returns the object implicitly.
Default binding: A function called without a receiver has this === undefined in strict mode, or the global object in sloppy mode.
Arrow functions (lexical binding): Arrow functions capture this from their enclosing scope at definition time and never change. Ideal for event handlers and callbacks inside class methods.
In Page Objects: Using arrow class fields (increment = () => {}) ensures this is always the instance, even when the method is extracted and passed as a callback.
// EXAMPLE
const obj = {
name: "qa",
regular() { return this.name; },
arrow: () => this?.name, // 'this' is outer scope (module = undefined)
};
console.log(obj.regular()); // "qa"
const fn = obj.regular;
// fn(); // undefined or TypeError in strict mode
// Explicit binding
console.log(obj.regular.call({ name: "test" })); // "test"
// Class arrow field — safe for callbacks
class Counter {
count = 0;
increment = () => { this.count++; }; // always Counter instance
}
const c = new Counter();
const inc = c.increment; // extracting is safe
inc();
console.log(c.count); // 1