this is easy

this is easy

this keyword is an integral part of the Javascript language. Yet sometimes it gets confusing to detect its value under different scenarios. 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 a call-site?

To understand this binding, we must first understand call-site. To find out call-site you should 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`

call_site.png A view of 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 different types of binding which happen 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 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.

default_binding.png 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 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 fallback to default binding in case of the implicit lost.

Let's understand this by an example.

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

var obj = {
    a: 2,
    foo: foo,
}

var bar = obj.foo // function alias
var 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

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-site foo.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().

Summary

To summarise these are the points you should keep in mind while detecting value of this keyword

  • First, it checks whether the function is called with the 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 is called via context object (implicit binding).
  • Default global object (undefined in case of strict mode).

Did you find this article valuable?

Support Nikhil Mishra by becoming a sponsor. Any amount is appreciated!