FUNCTION EXECUTION CONTEXT AND this IN JAVASCRIPT
This note explains this in simple language.
You will learn:
- what function execution context means
- what
thismeans in the global context - what
thismeans in method context - how
call(),apply(), andbind()work - why context can be lost
- how arrow functions handle
this - a simple algorithm for finding
this
1. What is function execution context?
In JavaScript, this works like context in a sentence. Instead of repeating the object name again and again, you use this to refer to the object in whose context the function was called. That is why using the object name directly inside its own methods is considered a bad approach.
Example
const user = {
username: "Victor",
showName() {
console.log(this.username);
},
};
user.showName(); // "Victor"
Diagram 1. Basic idea of this
Object
│
├─ data
│ └─ username
└─ method
└─ showName()
Inside showName():
this.username
When showName() is called, this refers to the user object.
So:
this.username
means:
user.username
in this call.
2. Most important rule about this
The value of this is defined when the function is called, not when the function is declared.
That means:
this depends on how the function is called
not where it was written
This is one of the most important rules in the whole topic.
Diagram 2. Main rule
Function declaration
↓
does not fix this
Function call
↓
defines this
So if the same function is called in different ways, this can have different values.
3. Global context
If a regular function is called by itself, without an object before it, then this depends on strict mode.
Example without strict mode
function foo() {
console.log(this);
}
foo(); // window
In browser scripts without strict mode, this refers to window.
Diagram 3. Global call without strict mode
foo()
↓
regular function call
↓
no object before dot
↓
this = window
Example with strict mode
"use strict";
function foo() {
console.log(this);
}
foo(); // undefined
In strict mode, this becomes undefined.
Diagram 4. Global call with strict mode
"use strict"
foo()
↓
regular function call
↓
this = undefined
So in the global context for regular functions:
- without strict mode →
window - with strict mode →
undefined
4. Method context
If a function is called as an object method, this points to the object before the dot.
Example
const user = {
username: "Benjamin",
showThis() {
console.log(this);
},
};
user.showThis(); // { username: "Benjamin", showThis: f }
Diagram 5. Method call
user.showThis()
│
├─ object before dot → user
└─ this = user
When a function is called like this:
object.method()
then this is the object before the dot.
5. Same function, different context
A function can be created in one place and later assigned as a method of an object. Even then, this is still defined by the call, not by where the function was created.
Example
"use strict";
function showThis() {
console.log("this in showThis:", this);
}
const user = {
username: "Benjamin",
};
user.showContext = showThis;
user.showContext(); // this in showThis: { username: "Benjamin", showContext: f }
showThis(); // this in showThis: undefined
Diagram 6. Same function, two calls
showThis created once
Call 1:
user.showContext()
↓
this = user
Call 2:
showThis()
↓
this = undefined (in strict mode)
This proves the main rule again:
this depends on the call
not on the place where the function was written.
6. call()
Sometimes you want to run a function immediately and manually choose what this should be.
For that, JavaScript provides call().
Syntax
foo.call(thisArg, arg1, arg2, ...)
Example
function greet(str) {
console.log(`${str}, ${this.username}, your room is ${this.room}!`);
}
const aaron = {
username: "Aaron",
room: 27,
};
const benjamin = {
username: "Benjamin",
room: 191,
};
greet.call(aaron, "Welcome"); // "Welcome, Aaron, your room is 27!"
greet.call(benjamin, "Aloha"); // "Aloha, Benjamin, your room is 191!"
Diagram 7. How call() works
greet.call(aaron, "Welcome")
↓
run greet immediately
↓
this = aaron
↓
str = "Welcome"
call() does two things:
- runs the function immediately
- sets
thisto the object you pass first
7. apply()
apply() works like call(), but arguments are passed as an array.
Syntax
foo.apply(thisArg, [arg1, arg2, ...])
Example
function greet(str) {
console.log(`${str}, ${this.username}, your room is ${this.room}!`);
}
const aaron = {
username: "Aaron",
room: 27,
};
const benjamin = {
username: "Benjamin",
room: 191,
};
greet.apply(aaron, ["Welcome"]); // "Welcome, Aaron, your room is 27!"
greet.apply(benjamin, ["Aloha"]); // "Aloha, Benjamin, your room is 191!"
Diagram 8. call() vs apply()
call()
↓
arguments are passed one by one
apply()
↓
arguments are passed in an array
The main difference is only the way arguments are passed. Both methods run the function immediately.
8. bind()
bind() is different from call() and apply().
call() and apply() run the function immediately. bind() does not run the function immediately.
Instead, it creates and returns a new function with fixed this. That new function can be called later.
Syntax
const boundFoo = foo.bind(thisArg, arg1, arg2, ...)
Diagram 9. What bind() does
bind()
↓
does not run now
↓
creates new function
↓
new function has fixed this
This is the key difference:
call()→ run nowapply()→ run nowbind()→ create new function for later
9. Losing context
A common problem in JavaScript is losing context.
This usually happens when you take a method out of an object and save it in a variable. Then it is no longer called as an object method.
Example
"use strict";
const customer = {
username: "Jacob",
sayHello() {
console.log(`Hello, ${this.username}!`);
},
};
customer.sayHello(); // "Hello, Jacob!"
const greet = customer.sayHello;
greet(); // TypeError
Diagram 10. Context loss
customer.sayHello()
↓
called as object method
↓
this = customer
const greet = customer.sayHello
greet()
↓
called as plain function
↓
this is lost
In strict mode, this becomes undefined, so:
this.username
causes an error.
10. Fixing lost context with bind()
You can fix this problem by creating a bound copy of the method.
Example
"use strict";
const customer = {
username: "Jacob",
sayHello() {
console.log(`Hello, ${this.username}!`);
},
};
const greet = customer.sayHello.bind(customer);
greet(); // "Hello, Jacob!"
Diagram 11. Fix with bind()
customer.sayHello.bind(customer)
↓
new function
↓
this is fixed to customer
↓
greet()
↓
works correctly
Now even if greet() is called by itself, the bound function remembers:
this = customer
11. bind() and callbacks
bind() is especially useful with callbacks, because passing a method as a callback often causes context loss.
Example with problem
"use strict";
const customer = {
firstName: "Jacob",
lastName: "Cohen",
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
};
function makeMessage(callback) {
const username = callback();
console.log(`Processing an application from ${username}`);
}
makeMessage(customer.getFullName); // TypeError
Diagram 12. Callback problem
customer.getFullName
↓
passed as callback
↓
called later without customer
↓
this is lost
The method is no longer called like:
customer.getFullName()
So the object context disappears.
12. bind() and callbacks — fix
Correct version
const customer = {
firstName: "Jacob",
lastName: "Cohen",
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
};
function makeMessage(callback) {
const username = callback();
console.log(`Processing an application from ${username}`);
}
makeMessage(customer.getFullName.bind(customer));
// "Processing an application from Jacob Cohen"
Diagram 13. Callback fix
customer.getFullName.bind(customer)
↓
bound copy
↓
callback()
↓
this stays customer
This is one of the most practical uses of bind().
13. Another bind() example
const library = {
books: 1923,
logBookCount() {
console.log(this.books);
},
};
const showBooks = library.logBookCount.bind({ books: 724 });
showBooks(); // 724
Diagram 14. Binding to a different object
library.logBookCount.bind({ books: 724 })
↓
new function
↓
this = { books: 724 }
↓
showBooks()
↓
724
bind() can fix this to any object you choose.
14. Arrow functions and this
Arrow functions work differently.
Inside an arrow function, this is defined by the place where the arrow was declared, not by the place where it was called.
That means arrow functions do not get their own call-time this. They use this from the outer scope.
Example
const showThis = () => {
console.log("this in showThis:", this);
};
showThis(); // window
Diagram 15. Arrow function this
Arrow function
↓
this is taken from outer scope
↓
call does not change it
This is the biggest difference between:
- regular functions
- arrow functions
15. Arrow function as object property
Even if you assign an arrow function to an object property and call it like a method, this still stays the same as when the arrow was created.
Example
const showThis = () => {
console.log("this in showThis:", this);
};
const user = {
username: "Aaron",
};
user.showContext = showThis;
user.showContext(); // window
Diagram 16. Arrow does not become method context
Arrow created in outer scope
↓
later attached to object
↓
called as user.showContext()
↓
this does not become user
This is why arrow functions are usually not used as object methods when you need normal method this.
16. Arrow functions and strict mode note
In the uploaded source notes, arrows declared globally in browser context are shown as still using window, even in strict mode examples. That is how the provided lesson explains those browser examples.
Example from the notes
"use strict";
const showThis = () => {
console.log("this in showThis:", this);
};
showThis(); // window
17. Very useful arrow example inside a method
const hotel = {
username: "Resort hotel",
showThis() {
const foo = () => {
console.log("this in foo:", this);
};
foo();
console.log("this in showThis:", this);
},
};
hotel.showThis();
Result in the notes:
this in foo: { username: "Resort hotel", showThis: f }
this in showThis: { username: "Resort hotel", showThis: f }
Diagram 17. Arrow remembers outer this
hotel.showThis()
↓
inside showThis, this = hotel
foo is arrow function
↓
foo takes this from showThis
↓
this in foo = hotel
This is a very common useful pattern:
- regular method gets
this - inner arrow function keeps that same
this
18. call(), apply(), and bind() do not change arrow this
Unlike regular functions, you cannot change this inside an arrow after it is declared. So call(), apply(), and bind() do not affect this in arrow functions.
Example
const showThis = () => {
console.log("this in showThis:", this);
};
showThis.call({ username: "Aaron" }); // window
showThis.apply({ username: "Aaron" }); // window
const boundShowThis = showThis.bind({ username: "Aaron" });
boundShowThis(); // window
Diagram 18. Arrow this cannot be rebound
Arrow function
↓
this fixed from outer scope
↓
call/apply/bind
↓
do not change it
This is a rule worth remembering very well.
19. What to remember about this in arrow functions
thisinside an arrow function is determined by where it was declared, not where it was called.- In the provided notes, arrows in global browser context still use
window. - You cannot change
thisinside an arrow function after declaration.call(),apply(), andbind()do not change it.
20. Algorithm for finding this
This algorithm from your note is very useful.
Diagram 19. Algorithm for finding this
Is this an arrow function?
│
├─ YES → this = this in outer scope
│
└─ NO
│
▼
Are call, apply, or bind used?
│
├─ YES → this = object passed there
│
└─ NO
│
▼
Is the function called as a method: obj.foo()?
│
├─ YES → this = object before the dot
│
└─ NO
│
▼
Does the script run in strict mode?
│
├─ YES → this = undefined
│
└─ NO → this = window
This step-by-step rule is directly based on the uploaded source.
21. Short version of the algorithm
- Is it an arrow function? If yes,
thiscomes from the outer scope. - Are
call(),apply(), orbind()used? If yes,thisis the object passed there. - Is it called like
object.method()? If yes,thisis the object before the dot. - Otherwise, for regular functions, check strict mode: in strict mode
thisisundefined, otherwisewindow.
22. Common beginner mistakes
Mistake 1. Thinking this depends on where the function was written
It does not.
It depends on how the function is called.
Mistake 2. Using object name inside its own methods
Better:
this.username
not:
user.username
because this is safer and works with the actual calling object.
Mistake 3. Losing context by saving a method into a variable
const greet = customer.sayHello;
greet();
This loses object context. Use bind() if needed.
Mistake 4. Expecting arrow functions to behave like regular methods
Arrow functions do not get this from the call. They keep it from outer scope.
23. Quick summary
Regular function, plain call
- strict mode →
this = undefined - non-strict browser script →
this = window
Regular function, method call
this = object before the dot
call() / apply()
- run immediately
- set
thismanually
bind()
- does not run immediately
- returns a new function with fixed
this
Arrow function
thiscomes from outer scopecall(),apply(), andbind()do not change it
24. Final conclusion
If you remember these four ideas, this becomes much easier:
1. this depends on the call
2. object.method() → this is the object
3. bind() fixes lost context
4. arrow functions keep outer this
This whole note is based on the uploaded source note you shared.