‘this’: learning how to follow the call stack
this 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 helped me understand what
this is and isn’t and guidelines for figuring out what
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.
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.
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:
- 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
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
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
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
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
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
.apply(). These functions changes to
this refer to the dog object that we pass in.
It’s important to know that
.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!