this is easy

Nikhil Mishra
5 min readJun 20, 2021

--

this keyword is an integral part of the Javascript language. Yet sometimes it gets confusing to detect its value under different scenarios.

Person typing code on his MacBook
Photo by cottonbro from Pexels

In this article, I’ll explain ways by which you can find out what is the value of this keyword by analysing the call- site.

What is this?

this represents the binding that is made when a function is called and the site, where the function call is made, is known as call-site.

How to find call-site?

To understand this binding, we must first understand call-site. To find out call-site you must first inspect where the function is called from. But finding out the call site is not always straightforward, as some coding patterns conceal it.

Demonstration of call-stack and call-site.

As defined by MDN. A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function.

Let's understand this with the help of some examples.

function baz() {
//call-stack is: `baz`
//so, our call-site is in the global scope
console.log("baz")
bar() // ← call-site for `bar`
}

function bar() {
// call-stack is `baz` --> `bar`
// so, our call-site is `baz`
console.log("bar")
foo() // ←- call-site for `foo`
}

function foo() {
// call-stack is `baz` -> `bar` -> `foo`
// so, our call-site is in `bar`
console.log("foo")
}

baz() // ← call-site for `baz`

A view of the debugger tool of the chrome browser showing the call stack.

To view the call-stack in your browser just put the debugger on the first line of each function. When you run the page, the debugger will stop at the point where you have a debugger and show the functions called to reach this line.

Determining the value of this

Now that you have understood call-site we will look into a different type of binding which happens by calling the function in different ways.

Default Binding

Let's take an example

function foo() {
console.log(this.a)
}

var a = 2

// call-site
foo() //2

In the above example after examining the call-site we can notice that function call tofoo() is a standalone function call also known as a standalone function invocation.

The type of binding which happens during standalone function invocation comes under the category of default binding.

In the function foo() this.a resolves to a global variable a because this here belongs to the global scope as the default binding rules are applied here and our variable var a = 2 is also declared in the global scope.

Whenever a variable is declared in the global scope the global-object contains the property with the same name.

Note: If you change var a to let a in our example. You will get this.a as undefined. Because according to ti the JavaScript specs let and const doesn't get defined on windows object.

Implicit Binding

The implicit binding rules apply when the call-site has a context object.

function foo() {
console.log( this.a )
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2

The implicit binding rule states that when a function call is preceded by the object reference it’s that object that should be used for the function’s this binding. Which is obj in our case.

It's important to note that you may lose implicit binding and fall back to default binding in case of the implicit lost.

Let’s understand implicit lost by an example.

function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo,
}
var bar = obj.foo // function aliasvar a = "global variable"bar() // global variable

You may be thinking that in the above example implicit binding rule should apply because bar refers to obj.foo.

But as stated earlier while determining this, what matters is the call-site and the call-site here is bar() which is a stand-alone function call.

That's why the default binding rule applies here.

Explicit binding

function foo() {
console.log(this.a)
}
var obj = {
a: 2,
}
foo.call(obj)

We can use explicit binding to force a function call to have to use a particular object for this binding, without putting a property function reference on the object as we did in the case of implicit binding.

All function in JavaScript have call(), apply() and bind() methods available to them. We can use these functions to achieve explicit binding.

e.g

function foo() {
console.log(this.a)
}
var obj = {
a: 2,
}
foo.call(obj)

In call-sitefoo.call(obj) . The call function calls foo with this given as the first parameter to call() which is obj in our example.

So, in the function foo this will point to obj.

Invoking foo with explicit binding by foo.call(...) allows us to force its this be obj.

new binding

When a function is invoked with new keyword in front of it, a new type of binding occurs, known as new binding. These things happen when we call a function with new

  1. A brand new object is created.
  2. The newly constructed object is linked to the Prototype chain.
  3. The newly created object is set as the this binding for that function call.
  4. The new invoked function call will return a newly constructed object.

e.g

function foo(a) {
this.a = a
}
var bar = new foo(2)console.log(bar.a) //2

In the above example, we have called the foo() with new operator in front of it.

This call will create and return a new object bar which is the this for the call to foo().

Precedence of this keyword bindings

  • First, it checks whether the function is called with new keyword.
  • Second, it checks for explicit binding by checking if the function is called with call(), apply() or bind().
  • Third, it checks if the function called via context object (implicit binding).
  • Default global object (undefined in case of strict mode).

I hope this article has helped in understanding the concept of this to you, if it did please don't forget to hit the clap. 👏

Originally published at https://asyncawait.co.

--

--

Nikhil Mishra
Nikhil Mishra

No responses yet