Recommended resources
The freely available resources this lesson will direct you to.
Hello again! Last lesson, you used lexical scope to work out which variables a callback retains. Today’s question is different: when a function uses this, which object—if any—does it refer to? This distinction comes up when a method is passed to a callback, or when an arrow function appears inside an object.
By the end, you’ll have a reliable way to trace this from a call site, explain why a detached method can fail, and decide when an arrow function is the right fix.
Regular functions: look at the call, not the definition
Consider one function used by two objects:
function getName() {
"use strict";
return this.name;
}
const api = { name: "API", getName };
const admin = { name: "Admin", getName };
console.log(api.getName()); // "API"
console.log(admin.getName()); // "Admin"
The function’s source code is identical in both calls. What changes is how it is invoked. In api.getName(), the object used to access the function is api, so this is api. In admin.getName(), it is admin.
This differs from the closure rule you just learned. Lexical scope determines which outer variables getName can access based on where it was created. For a regular function, the call determines its this value. An object literal does not permanently attach this to a function merely by storing that function in a property.
Read MDN’s this reference for the underlying call-site rule. The relevant material is in Description → Function context; you do not need the later material on classes or global context.
MDN explains how an ordinary function receives its this value and what changes when it is called without an object.
In “Function context,” begin at the paragraph opening “Inside a function” and read the call-site explanation, including the example where two objects use the same function. Farther down, start at “If the function is called without being accessed on anything” and read the strict-mode distinction. Finally, read the final paragraph immediately before “Callbacks” for call, apply, and bind. Focus on what happens at invocation, rather than where the function was first stored.
The “object before the dot” is a useful shorthand, but it means the object at the call site, not necessarily the object where the function was defined:
const team = {
name: "Backend",
label() {
return this.name;
},
};
const project = {
name: "Dashboard",
label: team.label,
};
console.log(team.label()); // "Backend"
console.log(project.label()); // "Dashboard"
Even though label was written inside team, project.label() supplies project as this. The same principle applies to a longer access path: in account.profile.label(), the receiver is account.profile, not account.
Detaching a method changes the call
Now remove the object from the invocation:
const user = {
name: "Maya",
getName() {
"use strict";
return this.name;
},
};
const readName = user.getName;
console.log(user.getName()); // "Maya"
readName(); // TypeError: cannot read 'name' from undefined
Assigning user.getName to readName obtains the function; it does not invoke it. Later, readName() is a plain function call, so in this strict function, this is undefined. The error happens when its body tries to read this.name.
Strictness matters when predicting the exact result. A plain call to a non-strict ordinary function substitutes globalThis for this; a plain call to a strict ordinary function leaves it undefined. JavaScript modules are strict, and many interview snippets explicitly use "use strict". Rather than assuming every plain call refers to window, state the execution environment—or use an explicit strict function, as we have here.
Passing a method as a callback often causes the same detachment:
function invoke(callback) {
return callback(); // A plain call, regardless of where callback came from
}
invoke(user.getName); // TypeError
The important line is callback(), not user.getName in the argument list. A callback has no automatic, permanent connection to the object from which it was obtained.
Watch Gordon Zhu trace extracted methods and callbacks in “this” keyword in JavaScript - Full Course for Beginners. His demonstrations use browser examples that are often non-strict, so concentrate on locating the actual invocation; use the strict-mode distinction above when translating a result into module code.
"this" keyword in JavaScript - Full Course for Beginners
Gordon Zhu shows how a method’s this changes when the function is extracted or passed elsewhere, then introduces ways to set it explicitly.
Watch extraction and callbacks: pause mentally at each invocation and identify the expression that actually calls the function. Continue with explicit binding for the roles of call, apply, and bind. Some examples involve class methods; for now, focus on the shared call-site reasoning.
If you need to pass user.getName somewhere while preserving user, you have two common options. A wrapper makes a method call when the callback runs:
invoke(() => user.getName()); // "Maya"
Alternatively, bind creates a new function whose this is fixed to user:
const boundReadName = user.getName.bind(user);
invoke(boundReadName); // "Maya"
For a one-off invocation, call and apply supply this directly. They differ in how they receive ordinary arguments:
function greet(greeting) {
"use strict";
return `${greeting}, ${this.name}`;
}
console.log(greet.call(user, "Hello")); // "Hello, Maya"
console.log(greet.apply(user, ["Hello"])); // "Hello, Maya"
call invokes with individually listed arguments; apply invokes with an array-like collection of arguments. bind does not invoke immediately: it returns a function for later use. These tools change this for ordinary functions, not the outer variables available through lexical scope.
Arrows take this from their enclosing context
Arrow functions are the key exception to the regular-function call-site rule. An arrow does not create its own this binding. Instead, it uses the this of the surrounding context in which it was created. Calling that same arrow through another object does not change the value.
Watch Zhu’s arrow-function example, then read MDN’s “Arrow functions” subsection. The video runs in a browser context, where its outer this is window; module-level this is different, so avoid memorizing window as an arrow-function rule.
"this" keyword in JavaScript - Full Course for Beginners
Gordon Zhu’s arrow-function example contrasts the object used to call an arrow with the surrounding this it retains.
Watch the arrow case. Locate where the arrow is created, then identify this outside it at that location. The browser’s particular outer value is less important than that method-style invocation does not replace it.
MDN’s “Arrow functions” subsection states the lexical rule and explains why explicit binding cannot override an arrow’s this.
Under “Function context → Arrow functions,” begin at “In other words, when evaluating an arrow function’s body.” Read the arrow explanation through the paragraph about call, bind, and apply. Focus on the enclosing context, rather than the global-code example’s particular value.
Here is a predictable example that does not depend on whether you run it in a browser script or a module:
function makeUser() {
"use strict";
return {
name: "Ava",
regular() {
return this.name;
},
arrow: () => this,
};
}
const user = makeUser();
console.log(user.regular()); // "Ava"
console.log(user.arrow()); // undefined
console.log(user.arrow.call({ name: "Lee" })); // undefined
makeUser() is a plain call to a strict function, so its this is undefined. The arrow is created during that call and uses that enclosing this. The returned object does not become the arrow’s this, even when we call user.arrow(). Passing another object with call does not change it either.
That makes an arrow a poor default for an object method that needs to use the object as this. Yet arrows are useful inside regular methods:
const user = {
name: "Ava",
makeReader() {
return () => this.name;
},
};
const readAva = user.makeReader();
console.log(readAva()); // "Ava"
console.log(readAva.call({ name: "Lee" })); // "Ava"
user.makeReader() sets this to user for the regular method call. The arrow created inside it retains that this, much as last lesson’s closure retained access to an enclosing variable. Later calls to readAva cannot replace its this.
Notice the limit: if makeReader itself were detached and called without user, the outer method would not receive user as this. An inner arrow preserves the outer value; it cannot repair an outer call that already has the wrong value.
A compact call-site checklist
When an interview snippet mixes objects, callbacks, and arrows, find the function containing this, then locate how that function—or its enclosing function, for an arrow—is used:
- Regular function called as
object.method()?thisis the object used for that call. - Regular function called without a receiver? Its
thisisundefinedin strict mode; non-strict functions substituteglobalThis. - Regular function invoked with
callorapply, or created withbind? Use the suppliedthisvalue. - Arrow function? Ignore its invocation style for
this; inspect the enclosingthiswhere the arrow was created.
One additional call style is new. In new User("Ava"), a regular constructor receives the newly created instance as this; an arrow function cannot be used as a constructor. You need not study class mechanics to apply today’s main rule, but recognizing new prevents you from treating a constructor invocation as a plain function call.
In Node or React code, this checklist is especially useful before passing an object method to a timer, event handler, or another function. Ask who invokes the callback, and how? If you need the original object, make the intended method call in a wrapper or pass a bound function rather than assuming the callback keeps its former receiver.
Key takeaways
For a regular function, this generally follows its call style, not its definition location. Extracting a method removes its method-style invocation; call, apply, bind, or a wrapper can provide the receiver you intend. An arrow function instead uses the this of its enclosing context, which is why it works well inside a correctly called method but not as a replacement for every method.
Next, we’ll combine functions and callbacks into promise-based workflows, using async/await while keeping error propagation intact.