'this' in javascript - Part Two

'this' in javascript - Part Two

A detailed explanation of 'this' concept in javascript

Β·

6 min read

Hello everyone, I hope you've read the first part of the article. In this part, we're going to learn about explicit binding, new binding, and this in arrow functions. Let's get started now πŸš€πŸš€

3. Explicit Binding (Indirect Invocation)

Explicit Binding is performed when we want to force a function call to reference a particular object for this binding. Then, we don't have to reference the object before the function call. Most of the functions in javascript have access to .call(...) and .apply(...), so what are their utilities?

Both take an object as their first parameter which they will use for this while invoking the function. Both are almost identical except the fact that .call(...) takes an argument list while .apply(...) takes a single array of arguments. For example - myFunc.call(obj, arg1, arg2, ...) myFunc.apply(obj, [arg1, arg2, ...]).

'this' is the first argument in .call() and .apply() methods.

Let's see some of the basic examples of explicit binding.

const favoriteMovie = {
  name: "The Matrix"
}

function sayName(actor) {
  console.log(`${actor} is the lead in ${this.name} movie`)  
  // => "Keanu Reeves is the lead in The Matrix movie"
}

sayName.call(favoriteMovie, "Keanu Reeves")
sayName.apply(favoriteMovie, ["Keanu Reeves"])

// Using call or apply without passing the first argument
var myDog = "proton"
function sayDogName() {
  console.log(this.myDog)  // => proton
}

sayDogName.call()

3.1 Hard Binding

Sometimes, even after explicit binding, a function can lose its this binding because of reasons like using other frameworks. In that case, we have to hard bind the function using a utility in javascript called .bind(...).

.bind(thisArg, arg1, .....) accepts a thisArg as its first parameter It returns a new function that has the same code and scope, but a different context according to the this argument provided. It also accepts an optional list of arguments. The returned bound function cannot change its context by any means except the constructor invocation of the bound function - we will not cover this case here because it's not in the scope of this article if you want to learn about it you can check this link . Let's see some examples below πŸ‘‡

const favoriteMovie = {
  name: "The Matrix",
  sayName() {
    console.log(this === favoriteMovie)
    return this.name
  }
}

const extractMovieName = favoriteMovie.sayName.bind(favoriteMovie)  

setTimeout(extractMovieName, 2000) /* logs true */
setTimeout(favoriteMovie.sayName, 1000) /* logs false */

In the above example, extractMovieName is the returned function bounded by favoriteMovie object, hence even if we separate the method from the object it will always refer to its bound context which in this case is the favoriteMovie object. As you saw, hard binding solved the problem of separating the method from the object.

Note - When we pass null or undefined to above three utilities, this refer to the global object as it ignores these values.

4. new Binding (Constructor Invocation)

There is nothing fancy about constructors in javascript, they are just regular functions called with a new keyword in front of them. A javascript object is created at that time and this refers to the newly constructed object.

function bar(num1, num2) {
  this.num1 = num1;
  this.num2 = num2;
}

const foo = new bar(5, 10);
console.log(foo.num1, foo.num2) // => 5 10

In the above example, we have bound the this of bar function to foo object!

5. 'this' in arrow functions

this refers to the enclosing scope of the arrow function

The special thing about arrow functions is that they don't have their own context, they borrow the context from the enclosing scope(function or global scope). The binding of the arrow function can never be modified, they are bound forever with lexical this. This time, you people try to predict the output and then check below!

// First Example
var num = 5;
const showNum = () => {
  console.log(this.num)  // => first output
};

showNum()

// Second Example
const favoriteMovie = {
  name: "The Matrix"
}

function sayName() {
  setTimeout(() => {
    console.log(this.name) // second output
  }, 2000)
}

sayName.call(favoriteMovie)

// Third Example
const anotherFavoriteMovie = {
  name: "Pulp Fiction"
}

function foo() {
  return () => {
    console.log(this.name)
  }
}

const baz = foo.call(favoriteMovie) 
baz.call(anotherFavoriteMovie)  // => third output
  • First Example is very simple, showNum is declared in the global context hence it can access the num property which is also declared in the global context. So the output will be 5.
  • In the second example, this is inherited from the scope of sayName. We have bound the sayName function with favoriteMovie, hence even when we're accessing this inside the setTimeout function, it still refers to the object only. The output, in this case, will be The Matrix.
  • Third example is interesting, we are returning an arrow function from a regular function and then we tried to bind foo and baz with different objects. We all know that this in the arrow function will refer to the context of foo. When we are binding foo, this will refer to favoriteMovie object. So if you write - baz(), The Matrix will be printed as output. But even when we bind baz with anotherFavoriteMovie, this will still refer to the old favoriteMovie object because foo function is already bound to favoriteMovie object. Hence third output will also be The Matrix!

Conclusion

  • We can conclude from these two parts that the dominant factor while evaluating this is by looking how the function is invoked.
  • In the case of arrow functions, look at the scope of function where the arrow function is declared.
  • Also, don't forget the precedence of the binding. It goes as follows : new binding > explicit binding > implicit binding > default binding. It is impossible to change the binding of 'this' in arrow functions

I know it's difficult to wrap your head around this concept when you read it for the first time but give it some time and read about it in detail because it covers many side topics as well. If you want, you can check the below references to read more about "this" concept in javascript (highly recommended)

I hope your doubts got cleared in this two-part series because it is indeed a confusing yet very important topic in which developers pull their hairs. Now you have a good basic understanding of "this", I suggest you try hands around how to write polyfills, it will be a good experience.

In the end, don't forget to like the article and share it with your friends😊. Please leave feedback in the comment section, it will be good to know where I can improve.