Digging into "this" context in JavaScript

Digging into "this" context in JavaScript

JavaScript and this

I've had some confusions when studying debounce and throttle functions. In particular, I was confused about how this worked in those functions.

The confusion

Let's go over some code:

const dog = {
  name: "Rex",

  // When you write a method like this:
  bark() {
    console.log(`${this.name} says woof!`);
  },

  // JavaScript stores it roughly like this:
  // bark: function() {
  //     console.log(`${this.name} says woof!`);
  // }
};

We have an object dog with a method bark.

When you call dog.bark(), this will point to dog. So this.name will be "Rex".

What happens if you do this?

const justBark = dog.bark;
justBark();

This is where my confusion started. You would imagine this would point to dog again, but it doesn't.

How JavaScript functions work

Why is that?

If you look at the comments of the code above, JavaScript essentially stores the method bark as a function like this:

bark: function() {
    console.log(`${this.name} says woof!`);
}

One of the flexible things about JavaScript is that you decide what this points to. To me this was really confusing but that's how the language works.

Bark under the hood is just a function:

function bark() {
  console.log(`${this.name} says woof!`);
}

When you call justBark(), this points to the global object. In a browser, this points to window. If that doesn't have a name property, it'll be undefined.

JavaScript method call under the hood

Under the hood, a simplified version of how a method call works. Mind you, JavaScript internals are likely very different, but this is a simplified version:

// Let's recreate what JavaScript does when you call methods
// This is a simplified version of JavaScript's internal method lookup
function callMethod(obj: any, methodName: string) {
  // Step 1: Look up the function on the object
  const func = obj[methodName];
  console.log("Step 1: Found function:", func);

  // Step 2: When called with dot notation (obj.method()),
  // JavaScript automatically binds 'this' to the object
  console.log("Step 2: Calling function with 'this' set to:", obj);
  func.call(obj); // This is what happens behind the scenes
}

Final example

const dog1 = {
  name: "Rex",
  bark() {
    console.log(`${this.name} says woof!`);
  },
};

const dog2 = {
  name: "Barkley",
};

// This will log "Barkley says woof!"
dog1.bark.call(dog2);

A final example to demonstrate how this works. Methods in reality are just functions decoupled from objects. If you call dog1.bark(), JavaScript will internally do something like callMethod(dog1, "bark").

And if you do dog1.bark.call(dog2), JavaScript will internally do something like callMethod(dog2, "bark").

In this case, a cleaner example would be:

function bark() {
  console.log(`${this.name} says woof!`);
}

bark.call(dog2);

Now, we can reuse bark for different objects in a "clean" way. But, it's important to emphasize that it doesn't matter whether you explicitly define it as a standalone function or it'sa method of an object. Under the hood, it's just a function referring to the this context.