← Back

FUNCTION EXECUTION CONTEXT AND this IN JAVASCRIPT

This note explains this in simple language.

You will learn:

  1. what function execution context means
  2. what this means in the global context
  3. what this means in method context
  4. how call(), apply(), and bind() work
  5. why context can be lost
  6. how arrow functions handle this
  7. 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:

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:

  1. runs the function immediately
  2. sets this to 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:

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:

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:

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

  1. this inside an arrow function is determined by where it was declared, not where it was called.
  2. In the provided notes, arrows in global browser context still use window.
  3. You cannot change this inside an arrow function after declaration. call(), apply(), and bind() 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

  1. Is it an arrow function? If yes, this comes from the outer scope.
  2. Are call(), apply(), or bind() used? If yes, this is the object passed there.
  3. Is it called like object.method()? If yes, this is the object before the dot.
  4. Otherwise, for regular functions, check strict mode: in strict mode this is undefined, otherwise window.

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

Regular function, method call

call() / apply()

bind()

Arrow function

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.

← Back