Skip to main content

this keyword and call(), apply(), bind() methods

What is this in JavaScript?

Figuring out this in JavaScript can be very challenging. In JavaScript this behaves differently than other standard languages like Java, C++ etc. this points to the instance of the current object in these languages. But in JavaScript, the value of this is determined at the run time. Today we will be learning this keyword along with.call(),.apply() and .bind() methods.

this keyword

First, let's know why the value of this is determined at the run time in JavaScript.

In JavaScript this is the context of function execution, or better say the context of a function invocation. In JavaScript there are four types of function invocation:

  • Function invocation
  • Method invocation
  • Constructor invocation
  • Indirect invocation or Apply invocation

The value of this depends on any of these invocation patterns, thus most of the javascript developers find it confusing. Understanding this would definitely make you a better developer. So don't skip this let's learn this together.

Before we start let me remind you what happens when a function execution context is created.

When a function execution context is created the JavaScript engine supplies an additional two parameters: this and arguments to every function execution context. The value of this is determined by the invocation pattern.

That's it! The first step is done, now have to understand how this behaves with respect to these four invocation patterns.


this in Function invocation

Function invocation means

When a function is not the property of an object, then it is invoked as a function:

function invoation

function sayHi() {
console.log("Hello!");
}
sayHi(); // Function invocation pattern

When a function is invoked with this pattern, this is bound to the global object (or undefined in the case of strict mode).

A common mistake the JavaScript developers encounter with function invocation is believing that this value is lexically bounded to the inner functions (regular functions) as well.

Let me show you what I mean:

const person = {
firstName: "John",
lastName: "Doe",
getName: function () {
console.log(this === person); // true
function getFullName() {
console.log(this === person); // false
console.log(`${this.firstName} ${this.lastName}`);
}
return getFullName(); // Function invocation pattern
},
};

person.getName();
Output:
TypeError: Cannot read property 'firstName' of undefined

In the above example inside the outer function this is equal to the person object. Because person.getName() is a method invocation on an object thus this equal to the person. But getFullname() function defined inside the getName() function, at this point you might expect to have this as person object while invoking getFullName() function too.

But the getFullName() is a function invocation (not a method invocation). Thus, there this is the global object (or undefined in the case of strict mode). Though the outer function has this context as a person object, it does not have any influence on the inner function (in the case of regular function).

The problem can be solved using the arrow function:

const person = {
firstName: "John",
lastName: "Doe",
getName: function () {
console.log(this === person); // true
const getFullName = () => {
console.log(this === person); // true
console.log(`${this.firstName} ${this.lastName}`);
};
return getFullName(); // Function invocation pattern
},
};

person.getName();
Output:
John Doe
NOTE

The context of the inner function (except arrow functions) depends only on its own invocation type, but not on the outer function's context.

The problem is solved because the arrow function does not create its own execution context. It resolves this lexically.

There is another way to solve this issue by explicitly adding this context to a function (regular function) invocation with the help of .call() and .apply() methods. Let me show you:

const person = {
firstName: "John",
lastName: "Doe",
getName: function () {
console.log(this === person); // true
function getFullName() {
console.log(this === person); // true
console.log(`${this.firstName} ${this.lastName}`);
}
return getFullName.call(this); // Indirect invocation pattern
},
};

person.getName();

Here, the getFullName.call(this) function is explicitily setting the this context to its outer function's this context.

The first argument of Function.prototype.call(thisArg, ...args) method is the this context, rest are the arguments of the function. We will have more examples later.

Takeaway

this is the global object (or undefined in the case of strict mode) in a function invocation.


this in Method Invocation

Method invocation means

When a function is stored as a property of an object, we call it a method

When a method is invoked, this is bound to that object. If an invocation expression contains a refinement (dot expression or [subscript] expression) it is invoked as a method.

method invocation

const counter = {
count: 0,
increment: function () {
console.log((this.count += 1));
},
};

counter.increment(); // Method invocation pattern
counter.increment(); // Method invocation pattern
counter.increment(); // Method invocation pattern
Output:
1
2
3

A common mistake the JavaScript developers encounter with method invocation is that by extracting the method from an object into a separate variable. Let me show you what I mean:

const counter = {
count: 0,
increment: function () {
console.log((this.count += 1));
},
};
// Extracting increment() method
// Storing it in a separate variable.
const count = counter.increment;
count(); // Function invoation pattern
Output:
TypeError: Cannot read property 'count' of undefined

When the method is called count(), it detached from the original object, you might think that this is the object counter on which the method was defined.

If the method is called without an object then a function invocation happens, where this is the global object (or undefined in case of strict mode)

To resolve this issue we have to set the this context to the counter object explicitly, for that we have .call(), .apply() and .bind() methods. Generally for that situation .bind() method is more preferable. Let me show you what I mean:

const counter = {
count: 0,
increment: function () {
console.log((this.count += 1));
},
};
//Binding `this` context to the `counter` object
const count = counter.increment.bind(counter);
count();
count();
count();
Output:
1
2
3

Here, we are binding this value explicitly to the increment() method with the help of the .bind() method. This works exactly the same as the .call() method except for the indirect invoking. Basically .bind() method returns a function that can be invoked later.

There is another example of method extraction, even senior developers got deceived sometimes. Let me show you what I mean:

class Greet {
constructor(message) {
this.message = message;
}
greeting() {
console.log(this.message);
}
}
const hello = new Greet("Hello!"); // Constractor invocation pattern
setTimeout(hello.greeting, 1000);

You might believe that setTimeout(hello.greeting, 1000) will call the hello.greeting() which logs the greeting message from the hello object.

Unfortunately, the method is got separated from its object hello when passes as a parameter: setTimeout(hello.greeting).

The following cases are equivalent:

setTimeout(hello.greeting, 1000);
const extractedGreeting = hello.greeting;
setTimeout(extractedGreeting);

When the separated greeting() is invoked as a function, this is the global object (or undefined in the case of strict mode). So the message does not log correctly.

To solve the problem we use the .bind() method. If the separated method is bound with the hello object, the context problem will be resolved.

class Greet {
constructor(message) {
this.message = message;
}
greeting() {
console.log(this.message);
}
}
const hello = new Greet("Hello!"); // Constractor invocation pattern
setTimeout(hello.greeting.bind(hello), 1000);
Output:
Hello!

hello.greeting.bind(hello) returns a new function that executes exactly like greeting() but has this as hello even in a function invocation.

The above problem alternately can be resolved using the arrow function:

class Greet {
constructor(message) {
this.message = message;
}
greeting = () => {
console.log(this.message);
};
}
const hello = new Greet("Hello!"); // Constractor invocation pattern
setTimeout(hello.greeting, 1000);

The problem is resolved because the arrow function binds this context lexically.

Takeway

this is the object that owns the method in a method invocation.


this in Constrcator Invocation

Constractor invocation means

If a function is invoked with the new prefix, then a new object will be created, and this will be bunded to the new object.

constructor invocation

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

Person.prototype.getFullName = function () {
console.log(`${this.firstName} ${this.lastName}`);
};

const jane = new Person("Jane", "Doe"); // Constractor invocation pattern
jane.getFullName();

Here, the new Person() is making a constructor call where the this context is the jane object. With the help of Person.prototype we are inheriting the getFullName() method inside the Person constructor.

A common mistake the JavaScript developers encounter with constructor invocation is forgetting about the new keyword. The reason: some JavaScript functions create instances not only when invoked as a constructor, but also when invoked as functions (factory functions) Let me give you an example:

function Person() {}

function makeObject(Constractor) {
return new Constractor();
}

const bob = makeObject(Person); // Function invocation pattern
const rob = new Person(); // Constractor invocation pattern

console.log(bob instanceof Person); //true
console.log(rob instanceof Person); //true

A more practical example:

const reg1 = new RegExp("[0-9]"); //Constractor invocation pattern
const reg2 = RegExp("[0-9]"); // Function invocation pattern

console.log(reg1 instanceof RegExp); //true
console.log(reg2 instanceof RegExp); //true

Using a function invocation to create objects is a potential problem(excluding factory functions), because some contractors may omit the logic to initialize the object when the new keyword is missing. So, make sure to use the new keyword when a constructor function invocation is expected.

Let me show an example of how we got deceived:

function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function () {
console.log(`${this.firstName} ${this.lastName}`);
};

const jane = new Person("Jane", "Doe"); // Constractor invocation pattern
jane.getFullName(); // Logs "Jane Doe"

The moment we forget the new keyword the whole context changes because now the function is invoked as function invocation. Therefore, this is bound to the global object (or undefined in the case of strict mode). So remember to use the new keyword when it is expected.

const monika = Person("Monika", "Doe"); // Function invocation pattern
monika.getFullName();
Takeaway

this is the newly created object in a constructor invocation.


this in Indirect invocation (apply invocation)

Indirect invocation means

When a function is called using the .call() or .apply() method, it is invoked as an indirect invocation or apply invocation. We have already seen a few examples in our above examples. Do not confuse yourself with the method invocation pattern. We can call our functions with any of these methods, how? Because functions are first-class objects in JavaScript they can have their own properties and methods right? Yes, they do have.

The this is the first argument of .call(thisArg, ...args) or .apply(thisArg, [...args]) in an indirect invocation or apply invocation.

So far so good, we know that the JavaScript engine supplies this context to each function invocation or execution. With .call() and .apply() we can explicitly set the this context to any function (excluding arrow functions). Let me show you what I mean:

function greet(msg) {
console.log(`${msg}, ${this.name}`);
}

greet.call({ name: "Dyna" }, "Hi"); // Indirect invocation
greet.apply({ name: "Crockford" }, ["Hello"]); // Indirect invocation

Let's take another example:

function talk() {
console.log(this.sound);
}

const dog = {
sound: "woof woof!!",
};
const cat = {
sound: "mew mew!!",
};

//Indirect invocation pattern
talk.call(dog); // woof woof!!
talk.call(cat); // mew mew!!

Anyway you get the idea, the this value in the first argument in these .call(), .apply() methods.

We have another method that can change the this context explicitly as we did with these .call() and .apply() methods. We can bind this context with any function (excluding the arrow function) and invoke it as a bound function later. Sometimes it is very difficult to identify between a function invocation and a bound function.

Bound functions are usually used for attaching the context of this to a method that is separated from its object.

The this value is the first argument of the .bind(thisArg, ...args) method.

function Animal(name, sound) {
this.name = name;
this.sound = sound;
}

Animal.prototype.makeSound = function () {
console.log(this.sound);
};

const cat = new Animal("cat", "mew mew"); // Constractor invocation pattern
// Method separation
const catSound = cat.makeSound;
// calling separated method
catCound(); // TypeError

We already saw such examples, we fixed the problem using using .bind() method. The function catSound() invoked as a function invocation, thus the this value is bound to the global object. To rebound its context .bind() method is there to rescue. It not only bind the context but binds tightly, even .call() and .apply() cannot change its value. Let's take an example:

const cat = new Animal("cat", "mew mew");
const dog = new Animal("Dog", "woof woof");
const catSound = cat.makeSound.bind(cat);

catSound.call(dog); // mew mew
catSound.apply(dog); // mew mew

this in Arrow function (Bonus)

Arrow functions treat this keyword differently. They don't define their context since it doesn't have its own this context. Arrow function resolves this lexically or in other words it inherits from the parent scope whenever you call this.

So, this is the enclosing context where the arrow function is defined.

A common mistake the JavaScript developers encounter with this in the Arrow function by declaring method (excluding class-based methods) with an arrow function. Let me show you what I mean:

function Animal(name, sound) {
this.name = name;
this.sound = sound;
}

Animal.prototype.makeSound = () => {
console.log(this.sound);
};

const cat = new Animal("cat", "mew mew");
cat.makeSound(); // Method invocation pattern

You might believe that the makeSound() method has this context to its object cat. No! the makeSound() is an arrow function and it is defined at the global scope thus this context will be the global object (or undefined in case of strict mode).

Though the makeSound() invoked as method invocation, the this context remains unchanged. The invocation pattern has no influence on the arrow function. Arrow functions resolve this lexically. So whenever you working with methods it must be declared using a regular function (excluding class-based methods) and for the inner functions you are free to use the arrow function, it will unnecessary this context binding.

So, finally, we understand how this behaves with respect to different invocation patterns, and we also understand how arrow functions are different from the regular functions.

Conclusion

For the regular functions, this value has an influence on the invocation patterns.

  • In function invocation this context bounds the global object.
  • In method invocation this context bounds that object.
  • In constructor invocation this context bonds that newly created object.
  • In indirect invocation this value is the first argument.
  • In arrow function this resolves lexically

Resources

Articles

Videos