Welcome back. In the last lesson, you saw that closures preserve access to variables from the place where a function was created. The keyword this looks related because arrow functions also capture it from an outer context—but regular functions behave very differently.
By the end of this lesson, you should be able to inspect a function call and determine what this means in it. This is essential for debugging object methods, callbacks, DOM event handlers, and older class-based code you may encounter in full-stack projects.
this is not ordinary lexical scope
With a closure, JavaScript finds variables such as status by looking outward from the place a function was written. That is lexical scope.
For a regular function, this does not follow that rule. Its value is normally decided at the moment the function is called. A useful model is to treat this as a hidden value JavaScript supplies to the function call.
function showThis() {
console.log(this);
}
The function definition alone does not tell you what this will be. You must find the call:
const user = {
name: "Asha",
showThis
};
user.showThis();
Here, this inside showThis is user, because the function is called as:
user.showThis()
For regular functions, the core rule is:
In a call written as
object.method(),thisis the object used to make that call.
Read these focused sections before continuing. They establish the call-time rule and the contrast with arrow functions.
Read “Object methods, this” from javascript.info. It gives a concise, practical account of method calls, detached functions, and the special behavior of arrow functions.
Read the sections “this in methods,” “this is not bound,” and “Arrow functions have no this.” In the first section, follow the method example and notice why this.name is safer than referring directly to an outer variable such as user. In “this is not bound,” focus on the runtime principle: the same regular function can receive different this values. Finish with the arrow-functions section, from the arrow distinction, and connect it to the closure model from the previous lesson.
Regular functions: inspect the call site
Consider an object method:
const order = {
id: "ORD-4821",
total: 1499,
printSummary() {
console.log(`${this.id}: ₹${this.total}`);
}
};
order.printSummary();
// ORD-4821: ₹1499
The method is a regular function written with method shorthand. Since it is called through order.printSummary(), this is order.
The important word is called. this is not permanently attached to the object where a function was first written.
function printName() {
console.log(this.name);
}
const customer = { name: "Meera", printName };
const admin = { name: "Ravi", printName };
customer.printName();
// Meera
admin.printName();
// Ravi
There is only one printName function. Yet each call gives it a different this value.
Follow the final call expression
When calls become more nested, look at the object immediately before the final method call:
const store = {
manager: {
name: "Priya",
introduce() {
return this.name;
}
}
};
console.log(store.manager.introduce());
// Priya
this is store.manager, not the outer store. The final call is made with store.manager.introduce().
This same idea applies to methods inherited through prototypes and to class instance methods later on: the relevant object is the one that actually makes the call.
Detached methods lose their object context
A very common bug appears when you take a method from an object and call it separately:
const session = {
userId: "u_42",
getUserId() {
return this.userId;
}
};
const readUserId = session.getUserId;
console.log(readUserId());
In ES modules and most modern application code, JavaScript runs in strict mode. A plain call like readUserId() gives a regular function this === undefined.
So this line attempts to read undefined.userId and throws a TypeError.
The assignment did not preserve the original receiver:
const readUserId = session.getUserId;
It copied a function value, not a function plus its this context.
This matters in server and frontend code whenever you pass a method as a callback:
router.get("/profile", userController.getProfile);
If getProfile relies on instance state through this, its behavior depends on how Express ultimately invokes it. Professional backend code often avoids this ambiguity by using standalone functions, closures, or deliberately bound methods.
Arrow functions capture outer this
An arrow function does not create its own this binding. Instead, it uses the this from the surrounding scope at the time the arrow is created.
That makes arrow functions behave like closures over this.
const cart = {
itemCount: 3,
printLater() {
setTimeout(() => {
console.log(this.itemCount);
}, 500);
}
};
cart.printLater();
// 3
Here is the reasoning:
cart.printLater()calls the regular method withthisequal tocart.- The arrow function is created inside
printLater. - The arrow has no separate
this, so it usesprintLater’sthis. - Therefore, the arrow reads
cart.itemCount.
If you replaced the arrow callback with a regular function, it would receive its own this, determined by however setTimeout invokes it. That value differs by environment, so you should not rely on it being the original cart.
The arrow version is reliable because it does not ask setTimeout for a new this value.
Watch this short demonstration, concentrating on the comparison inside a delayed callback rather than arrow syntax shortcuts.
JavaScript ES6 Arrow Functions Tutorial
Watch “JavaScript ES6 Arrow Functions Tutorial” by Web Dev Simplified. This segment illustrates the practical reason arrow callbacks are common in UI and asynchronous code: they retain an enclosing method’s this.
Watch the this comparison. Follow the two methods containing timeout callbacks. Pause when the presenter contrasts the regular callback and arrow callback, then identify which surrounding function supplies this to the arrow.
An arrow function is usually wrong as an object method
Because arrows capture outer this, they usually should not be used when an object method needs to refer to that object.
const profile = {
name: "Anil",
describe: () => {
return this.name;
}
};
It may look as though profile.describe() should return "Anil". It does not. The arrow was created in the surrounding scope, not inside a regular method call on profile.
In an ES module, top-level this is undefined, so calling profile.describe() attempts to access undefined.name and throws an error.
Write the method as a regular function instead:
const profile = {
name: "Anil",
describe() {
return this.name;
}
};
console.log(profile.describe());
// Anil
This distinction is worth memorizing:
| Function form | Where this comes from |
|---|---|
| Regular function | How the function is called |
| Arrow function | The surrounding lexical context where the arrow was created |
An arrow’s this cannot be changed later by calling it through another object.
const member = {
name: "Zoya"
};
const getOuterThis = () => this;
console.log(getOuterThis.call(member));
call() does invoke the arrow, but it cannot replace the arrow’s lexical this. The same is true for bind() and apply().
Callbacks: do not assume they retain the original object
The detached-method issue becomes especially visible in callbacks. Compare these two methods:
function runTask(callback) {
callback();
}
const dashboard = {
title: "Sales overview",
logWithRegularFunction() {
runTask(function () {
console.log(this.title);
});
},
logWithArrowFunction() {
runTask(() => {
console.log(this.title);
});
}
};
Now consider the calls:
dashboard.logWithRegularFunction();
The callback is ultimately called as callback(). In strict mode, this inside that regular callback is undefined, so this.title fails.
But:
dashboard.logWithArrowFunction();
// Sales overview
When dashboard.logWithArrowFunction() begins, its regular-method this is dashboard. The arrow callback captures that value and retains it when runTask later calls the callback.
The word “callback” itself does not determine this. A callback can be invoked in many ways. You must either:
- inspect how the API calls it,
- read the API documentation, or
- use an arrow when you intentionally want to retain surrounding
this.
DOM event listeners are a useful special case
In a browser, a regular event-listener function is called with this set to the element whose listener is running.
const saveButton = document.querySelector("#save-button");
saveButton.addEventListener("click", function (event) {
console.log(this === event.currentTarget);
// true
});
With an arrow listener, this is still lexical and is not changed to the button:
saveButton.addEventListener("click", (event) => {
console.log(event.currentTarget);
});
For event handlers, event.currentTarget is often clearer than relying on this, especially in React-oriented code where event callbacks are commonly arrows.
Deliberately setting this
Sometimes you really do want to choose a regular function’s this. JavaScript provides call, apply, and bind.
function formatUser(prefix) {
return `${prefix}: ${this.name}`;
}
const member = { name: "Fatima" };
console.log(formatUser.call(member, "Member"));
// Member: Fatima
call() invokes the function immediately and explicitly supplies member as this.
bind() creates a new function that will always use the chosen this:
const formatMember = formatUser.bind(member);
console.log(formatMember("Signed in user"));
// Signed in user: Fatima
This can repair a detached method:
const getUserId = session.getUserId.bind(session);
console.log(getUserId());
// u_42
Use binding intentionally, not as a reflex. In modern React function components and Express route handlers, plain functions with explicit parameters often reduce the need for this. But you will still encounter bind, especially in class-based JavaScript code and third-party libraries.
For completeness, new also gives a regular function its this value:
function User(name) {
this.name = name;
}
const user = new User("Karan");
console.log(user.name);
// Karan
During new User("Karan"), this refers to the new object being constructed. Arrow functions cannot be used as constructors because they do not have their own this.
A practical tracing routine
When you see this, use this order rather than relying on where a function appears in the file:
- Identify the function type. Is it a regular function or an arrow?
- For an arrow, find its surrounding
this. Keep moving outward until you find a regular function, constructor, or relevant enclosing context. - For a regular function, locate its actual invocation. Do not stop at an assignment or callback registration.
- Check for explicit binding.
call,apply, andbindoverride the ordinary regular-function call rule. - Check for construction. A regular function invoked with
newreceives the new instance asthis. - For a plain regular call in modern module code, expect
thisto beundefined.
Try this short console experiment in a JavaScript module or a modern build-tool project:
const account = {
owner: "Nikhil",
regularMethod() {
return this.owner;
},
outerMethod() {
const arrow = () => this.owner;
return arrow();
}
};
const detached = account.regularMethod;
console.log(account.regularMethod());
console.log(account.outerMethod());
console.log(detached());
Before running it, trace each call using the routine above. The first two calls return "Nikhil"; the third fails because detached() is a plain regular-function call.
Key takeaways
- Regular functions receive
thisprimarily from their call site. - In
object.method(),thisisobject. - Assigning a method to a variable or passing it as a callback can detach it from its original object.
- In strict-mode module code, a plain regular function call has
this === undefined. - Arrow functions have no own
this. They capturethisfrom the surrounding context, much like they close over surrounding variables. - Use regular methods when an object method needs its own receiver; use arrow callbacks when you want to preserve an enclosing method’s
this. call,apply, andbindexplicitly controlthisfor regular functions, but not for arrows.
Next, you will use the Fetch API to consume a JSON REST endpoint—the point where browser-side JavaScript begins communicating directly with your backend API.
Can't find a good explanation? Sign up and we'll make it for you
Sign up