Understanding call and this in JavaScript

Understanding call and this in JavaScript

In JavaScript, the this keyword provides a reference to the current execution context. However, when functions are nested, determining the correct this context can be challenging. Let’s explore this concept with a detailed example.

Nested Functions and this Context

Consider a scenario where a function is called within another function:

function outerFunction() {
  innerFunction();
}

In this example, innerFunction doesn't have a clear reference to this within outerFunction. Here's a visualization of the call stack:

and below callMe(), we have an execution context of the parent function.

In such cases, this typically refers to the Global Execution Context (GEC). In a browser environment, this means the window object, while in a Node.js environment, it refers to an empty object.

Example: Setting User Details

Let's see how this works in practice:

function setUsername(username) {
  this.username = username;
  console.log("called");
}

function createUser(username, email, password) {
  setUsername(username);
  this.email = email;
  this.password = password;
}

const chai = new createUser("chai", "chai@fb.com", "123");
console.log(chai);

//outputs: createUser { email: 'chai@fb.com', password: '123' }

Notice that the username property is missing. This occurs because setUsername is called without a proper this context, causing it to refer to the GEC.

Also, the function is getting called, but after the function has done its work, it gets out of the call stack and the memory is released, Also we have given a reference to the function, now you will think that "setUsername(username)" is a function call in itself, but it's only the reference.

  • Therefore, in JS we get some methods through which we can explicitly call the functions, and one of those methods is "call".

  • Now if we only use "call", then output will be the same as earlier, as after the function has done its work, it gets out of the call stack and memory is released, therefore we don't get that variable inside the parent function.

  • Therefore we have to hold the reference and for this purpose, we can use "call" for explicitly calling and holding the reference.

  • Now, we also have to pass execution context, as we are saying to the child function that don't use your "this" as it is going to be released.

  • But use my "this" i.e of parent function, therefore we have written it like "setUsername.call(this, username)"

Explicitly Setting this with call

To resolve this issue, we can use the call method to explicitly set the this context of setUsername:

function setUsername(username) {
  this.username = username;
  console.log("called");
}

function createUser(username, email, password) {
  setUsername.call(this, username);
  this.email = email;
  this.password = password;
}

const chai = new createUser("chai", "chai@fb.com", "123");
console.log(chai);

//outputs:
called
createUser { username: 'chai', email: 'chai@fb.com', password: '123' }

By using "setUsername(this, username)", we ensure that setUsername uses the this context of createUser.

Significance of "new" keyword:

  • Object Creation: The new keyword creates a new empty object.

  • Prototype Linking: The new object's prototype is set to createUser.prototype.

  • Context (this): The this context inside createUser is bound to the new object.

  • SetUsername Call: SetUsername.call(this, username) sets the username property on the new object.

  • Property Setting: this.email and this.password are set on the new object.

  • Implicit Return: If no object is explicitly returned, the newly created object is returned.

  • Assignment: Therefore, chai is assigned the new object with the properties username, email, and password.

Key Points to Remember

  1. Global Execution Context (GEC): In a browser, this refers to the window object. In Node.js, it refers to an empty object.

  2. Function Calls: When a function is called inside another function, this may not refer to the intended context.

  3. Using call: The call method can explicitly set this for a function, ensuring it uses the correct context.

Understanding and using call effectively allows us to manage this context accurately, ensuring our functions behave as expected.