‘this’: learning how to follow the call stack

Learning Javascript’sthis mechanism has made me realize how often we all say the word ‘this’ in everyday language, making actually understanding this that much more confusing. this in Javascript allows us to more seamlessly pass along an object reference instead of explicitly having to define the object that you are referring to if you are reusing the same functions. You-Don’t-Know-JS’ chapter on this helped me understand what this is and isn’t and guidelines for figuring out what this is.

this isn’t what you think it is…

this is commonly misused as a bridge between lexical scopes (a function’s static scope). In lexical scopes, nested functions are able to access the variables of their parent function but not vice versa — aka a variable that is born in captivity, dies in captivity.

Left: nestedFunction is able to access the value of x outside of it, Right: outerFunction is unable to access the value of y from the nested function

In the code above, the nested function was able to access the variable of the outer function but not vice versa. this is commonly misused as a bridge between two functions’ lexical scopes. As seen below, the foo function is trying to reference the bar() function with this.bar() by using this to bridge the two functions so bar() can access the a variable in the foo scope. When the foo function is called, this refers to the window so then the bar() function gets called on the window and the this in that function also refers to the window which does not have an a attribute so returns undefined.

But getthis!

As seen in the example above, this is defined based on how the function is called or its call-site (the location in the code where the function is called). When a function is called the execution context is created which contains information including its this reference that will be used for the duration of that function’s execution. To figure out what this is referring to in a given function, we first need to figure out what the call stack is, or the stack of functions that have been called prior to getting to the function we’re investigating. While we can visually follow the call stack in our code, there are tools within the console to see what the this is referring to at a given point in the code execution. Using the same example you see that in both functions this is referring to the window.

There are a few main scenarios to keep in mind to help you figure out what this is referring to:

  1. Simple Function Calls

With a simple function call, this defaults to being the global object — the window. In the below example, foo() is being called straightforwardly without reference from another function so this refers to the window.

However if you use strict mode in your function, then this is undefined because in strict mode only variables that are explicitly defined can be used.

2. Implicit Binding

In the below example, you need to first look at the call site of foo(). The call site is referencing the foo function on the obj context so obj owns the this reference at function execution. When there is a context object for a function reference, then that is the object that should be used for the function call’s this binding.

Another example below shows that when you just call the function method without binding it to an object at execution, this refers to the Window. But when you call the same method on the object by defining obj.objMethod =method and then calling obj.objMethod() , this then refers to the object that the method is being called on at execution.

However, if you add another layer to the original method function by defining another variable equal to obj.objMethod(), this no longer refers to the object since the object is no longer the context object at execution.

3. Constructor Functions

When creating new objects using a constructor function, the newly created object becomes the object that this gets bound to at execution. That is why below, we are able to call Fido.name and this refers to the new Fido object based on the Dog prototype.

4. Method Calls on the Prototype Chain

When we create an object based on a prototype (such as p below), that new object can inherit the function attribute from up its prototype chain. Here, the object p has a function as an attribute that returns this in the context of the function. As we just saw in the implicit binding section when we called the function objMethod() on obj, this works the same when you are calling the function on the object “owns” it. this again refers to the object that the method is being called on at execution.

5. Explicit Binding

We can alter what this refers to by using .call().bind(), or .apply(). These functions changes to this refer to the dog object that we pass in.

It’s important to know that .call().bind() and .apply() work differently when using arrow functions. As seen below, we changed simpleFn() to an arrow function but still returning this. Similarly, when we just call arrowFunction() we get the Window returned. However, when we use .call() with our dog object, this still refers to the Window.

6. Nested Functions

With nested functions, this loses its binding to the object if it is not explicitly bound. As you can see in the example below, within the sayHi() function that is a property of the Dog object, this is still bound to the Dog object so we are able to call this.name and get “Fido”. However, once we enter the nested function, this becomes undefined.

To keep the this reference to the Dog object, we can explicitly bind it to the function and reference that object when we call the function.

this can be confusing but as long as you figure out the order of the call stack and where a function is actually called, this becomes much more clear. But if all else fails, use debugger and see what this is equal to in your console!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.