Categories
JavaScript

Messing with JavaScript This with Call, Bind and Apply

Spread the love

In JavaScript, the this object can mean different things in different contexts. It mostly depends on the location where the this keyword is located. If it’s in an object, then this is the object. If this is in a function, then this is the function. For classes, this would be the class since it’s just syntactic sugar for normal constructor functions. In JavaScript functions, there’s a call, bind, and apply methods to change the value of this inside a function. In this article, we’ll look at them closely and how to use them to change the value of this. These methods only apply to regular functions and not arrow functions.

call

The call method lets us call a function with the given this object that we pass into the argument when we call it and arguments for the function provided individually.

It takes multiple arguments. The first argument is an object that we want to set to be the value of this inside the function that we’re calling the call method on. It’s an optional argument. We can set it to an object, null or undefined . In non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects. The second and subsequent arguments are arguments that we pass into the function.

The return value of the call method is the same function it’s called on with the specified this value and arguments.

The purpose of the call method is that we can reuse the same function with a different this value without writing a new value with the this value that we want.

For example, we can change the value of this in a function with the call method by writing the following code:

let obj = {  
  firstName: 'Joe',  
  lastName: 'Smith'  
}

let person  = {  
  firstName: 'Jane',  
  lastName: 'Smith',  
  getFullName()  {  
    return `${this.firstName} ${this.lastName}`  
  }  
}

console.log(person.getFullName());  
console.log(person.getFullName.call(obj));

In the code above, when we call person.getFullName() we get ‘Jane Smith’ since we have firstName set to 'Jane' in the person object and lastName set to 'Smith' in the same object. Since this is the person object inside the getFullName function, we get back ‘Jane Smith’ as a result.

When we use the call method to change the this object inside the getFullName function to obj by calling person.getFullName.call(obj)) , we get ‘Joe Smith’, since we changed this to obj which has firstName set to 'Joe' and lastName set to ‘Smith’.

We can also use the call method for constructor functions. For example, if we have:

const Person = function(firstName, lastName) {  
  this.firstName = firstName;  
  this.lastName = lastName;  
}const Employee = function(firstName, lastName, employeeCode) {  
  this.employeeCode = employeeCode;  
  Person.call(this, firstName, lastName)  
}

const employee = new Employee('Jane', 'Smith', '123');  
console.log(`${employee.firstName} ${employee.lastName}`, employee.employeeCode);

Then in the Employee constructor, we call the Person constructor function that we previously defined. When we create a new Employee object, we pass in a new firstName and lastName , and then inside the Employee constructor, we used the call method of the Person function to pass the firstName and lastName to the Person constructor, thereby setting the value for the firstName and lastName properties of this without setting them in the Employee constructor directly.

When we run console.log on the last line, we get 'Jane Smith' and '123'.

Another example is that we can use call with anonymous functions. For example, we can write the following function to log a greeting:

(function(greeting) {  
  console.log(`${greeting} ${this.firstName} ${this.lastName}`);  
}.call({  
  firstName: 'Joe',  
  lastName: 'Smith'  
}, 'Hello'));

In the function call above, we pass in an object with the firstName and lastName properties to the first argument of the call method of the anonymous function. Then we pass in the string 'Hello' in the second argument, which is passed into the first parameter of the anonymous function. This means that 'Hello' is the value of the greeting parameter, and this.firstName is 'Joe' and this.lastName is 'Smith' .

So, we should get 'Hello Joe Smith’ when we run the code above.

apply

The apply method is almost the same as the call method, except that we pass in an array for the second argument as the arguments for the function call instead of a comma-separated list of objects.

This means we can replace call with apply in the examples above by making some small changes.

The first example would be the same except that we change call to apply since we didn’t pass in the second or subsequent arguments:

let obj = {  
  firstName: 'Joe',  
  lastName: 'Smith'  
}

let person  = {  
  firstName: 'Jane',  
  lastName: 'Smith',  
  getFullName()  {  
    return `${this.firstName} ${this.lastName}`  
  }  
}

console.log(person.getFullName());  
console.log(person.getFullName.apply(obj));

If we chain constructors with the apply method for constructor functions, then we have to change call to apply and change the arguments list into an array. For example, if we have:

const Person = function(firstName, lastName) {  
  this.firstName = firstName;  
  this.lastName = lastName;  
}
const Employee = function(firstName, lastName, employeeCode) {  
  this.employeeCode = employeeCode;  
  Person.call(this, firstName, lastName)  
}

const employee = new Employee('Jane', 'Smith', '123');  
console.log(`${employee.firstName} ${employee.lastName}`, employee.employeeCode);

like we did above, then we just have to change it to:

const Person = function(firstName, lastName) {  
  this.firstName = firstName;  
  this.lastName = lastName;  
}

const Employee = function(firstName, lastName, employeeCode) {  
  this.employeeCode = employeeCode;  
  Person.apply(this, [firstName, lastName])  
}

const employee = new Employee('Jane', 'Smith', '123');  
console.log(`${employee.firstName} ${employee.lastName}`, employee.employeeCode);

Then we get the same output as we did above.

Finally, for the last example, we can change the code from:

(function(greeting) {  
  console.log(`${greeting} ${this.firstName} ${this.lastName}`);  
}.call({  
  firstName: 'Joe',  
  lastName: 'Smith'  
}, 'Hello'));

to:

(function(greeting) {  
  console.log(`${greeting} ${this.firstName} ${this.lastName}`);  
}.apply({  
  firstName: 'Joe',  
  lastName: 'Smith'  
}, ['Hello']));

Then we get the same output as we did before the change.

bind

The bind method is only for changing the value of this in a function. Like call and apply , it can be used to send arguments into the function.

It takes multiple arguments. The first argument is an object that we want to set to be the value of this inside the function that we’re calling the bind method on. This argument is ignored if the bound function is constructed with the new operator. When we use the bind method to create a function that’s supplied as a callback inside the setTimeout function, then any primitive value passed into the first argument is converted to an object. If not arguments are provided to bind , the this value of the executing scope is treated as the value for the new function.

The second and subsequent arguments are arguments to prepend to the list of arguments when we call the function with bind .

The return value of the bind method is the same function it’s called on with the specified this value and arguments.

For example, if we call bind with no argument like in the following code:

let person = {  
  firstName: 'Jane',  
  lastName: 'Smith',  
  getName(){  
    return `${this.firstName} ${this.lastName}`  
  }  
}console.log(person.getName.bind()());

Then we get undefined undefined because this would be set to the window object since the this of the executing scope is a window object since it’s run at the top level.

If we call bind with an object passed in, like in the following code:

let person = {  
  firstName: 'Jane',  
  lastName: 'Smith',  
  getName() {  
    return `${this.firstName} ${this.lastName}`  
  }  
}

const joe = {  
  firstName: 'Joe',  
  lastName: 'Smith'  
}

console.log(person.getName.bind(joe)());

We get 'Joe Smith' since we passed in the joe object into the first argument of the bind method, which sets the this object inside the getName method to the joe object.

Also, we can get pass arguments into a function with bind. For example, we can add a greet method that takes a greeting and an age parameter and return a new string with all of them combined together with the existing fields as we have in the following code:

let person = {  
  firstName: 'Jane',  
  lastName: 'Smith',  
  getName() {  
    return `${this.firstName} ${this.lastName}`  
  },  
  greet(greeting, age) {  
    return `${greeting} ${this.firstName} ${this.lastName}. You're ${age} years old`  
  }  
}

const joe = {  
  firstName: 'Joe',  
  lastName: 'Smith'  
}

console.log(person.greet.bind(joe, 'Hello', 20)());

If we run the code above, then we get 'Hello Joe Smith. You’re 20 years old’ since we set this to joe by passing joe into the first argument of the bind method. Then we pass in our arguments to the person.greet method by passing in 'Hello' , and 20 as the second and third arguments of the bind method, which gets passed into the first and second argument of the greet method call.

We can also use it with the setTimeout function. According to the definition of bind that we have above, if we pass in a primitive value to the first argument of bind of the callback function for the setTimeout function, then it’ll be converted to an object. For example, if we have:

setTimeout(function(x, y) {  
  console.log(this, x, y);  
}.bind(1, 1, 1), 1)

Then we get Number {1} 1 1 from the console.log output in the callback function since we have the primitive value one in the first argument converted to an object, then the other 2 arguments are the arguments that are passed into the callback function when it’s called.

In JavaScript functions, there’s a call , bind , and apply methods to change the value of this inside a function. In this article, we’ll look at them closely and how to use them to change the value of this . They only apply to regular functions since arrow functions don’t change the value of this . All 3 are very similar in that they all change the value of this and let us pass in arguments to the function that they’re called on.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *