Recently I was working on a project with Angular 7 when a teammate told me about a problem he had within a component, the problem was that he subscribed to an observable returned by one service implemented in the application and within the callback in charge of receiving the values did not have access to an attribute defined in the component.
Specifically, the problem was on line 14 that data
isn't defined as property of this
🙄🤔.
After analyzing the code I told him that the problem wasn't related to Angular, and to understand the cause of it he should know how this
binding works in JavaScript. A few days after I told about this error to another colleague, and while I explain it I realized that he also didn't have a complete understanding of how this
work.
In the article
this
is used to represent the JavaScript keyword and not the English word
Currently JavaScript is within one of the most used languages worldwide, I think it's very common to find developers working with JavaScript that use this
without really understanding the basics of this feature that the language provides us. I think this is largely due to the introduction of pseudo-classes in ES6, since they try to imitate a similar syntax for the definition of classes to that of other languages, and therefore less experienced developers tend to associate this
in the same way that it works in other programming languages (my colleagues had worked with PHP and C# respectively).
this
binding
The keyword this
in JavaScript is automatically defined inside of the scope of any function f
, and within each f
we have that this
represents a given object. The problem really with this
is that the object represented is not defined by the way we implement the function, but is defined dynamically at run time depending on how we call the function, that is, the object represented by this hasn't nothing to do with where f
is declared, but it has to do with the way f
is called.
Simplifying we can say that the interpreter uses 5 rules to determine the object that this
represents within f
, we will explain each of these rules and then define their precedence levels.
Default binding
The first rule that we will examine is the simplest of all, and applies whenever one of the others is not applicable, so we can also say that it is the rule of least precedence.
The default binding is applied when a function f
is called in the form f()
. When this rule is applied this
points to the global scope, note that this has the consequence that if we modify this
within the function for example by inserting some property it will be accessible even after executing the function because it would be defined globally, for example:
It is valid to clarify that the variable name
within the global scope is accessible only in the case of browsers, for the case of Node on line 6 it would have been printed undefined
In the previous snippet it is exemplified as this
points to the global scope.
In the case that we execute our script in strict mode
at the time of applying the default binding the interpreter doesn't allow this to represent the global scope, therefore this will point to undefined. The previous snippet running in strict mode
throw the following error:
TypeError: Cannot read property 'name' of undefined
Implicit binding
The second rule or implicit binding is applied in the case that a function f
contained in an obj
object is executed using dot notation for its execution obj.f()
, example:
In the previous example we see how both objects contain printInfo
property that refers to the same function, but despite this when executing the function in one case this
represent the dwarf
object, while for the other it's threeEyesRaven
. This is because in each of the calls to the function an object is used, which we can name as context
, in this case the implicit binding define that within the function this
points to the context object, therefore saying this.name
would be the same as saying dwarf.name
or threeEyesRaven.name
depending on the object used in the call.
Lost Implicity
It's very common for some developers to lose at some point in the source code the implicit binding defined for some specific object, which means that the binding that is applied would be the default binding having this
pointing to the global scope or undefined.
This can happen when we use callbacks, for example:
What happens is that here we are passing directly to setTimeout
a reference to our function printInfo
without passing the object where it's contained, on the other hand we have no control of how setTimeout
call the function, to better understand what happens suppose this pseudo implementation of setTimeout
:
Analyzing the call-site of fn
in the previous snippet is easy to conclude that the default binding is applied and the explicit binding that was previously available is lost, because dot notation isn't used to call the function.
Explicit Binding
So far we have seen 2 rules to determine the value of this
within a function, the first applies when we call the function as standalone function and the second when the function is executed by accessing it as part of an object.
Next we will see another type of binding for the case in which we explicitly define the object to which this
points within a function, this type of binding is known as explicit binding.
To get into the explanation of this type of binding we must start talking about 2 methods present in every JavaScript functions, these methods are apply and call. Both methods take the object to be pointed by this
as the first parameter and then execute the function with this configuration. Because we directly indicate what will be the value for this
when executing the function we are in presence of explicit binding. For example:
In the previous example, we noticed how the first time we executed the print
function, it print "Rob Stark" because that is the value of the name property of kingInTheNorth
object which contains the print
function and therefore applying the implicit binding when executing the function this
will point to the object. The second time we execute the function then "Jon Snow" is printed instead of Rob Stark even though we are accessing to the same function contained in the kingInTheNorth object, what happens is that in the function's call-site we are calling the call
method and explicitly indicating that the function is executed using the newKing
object as this
, so in that case within the function this.name
refers to newKing.name
.
Explicit binding with bind
Sometimes is desirable to indicate this
for some function without executing it. For this case, each function has a bind
method which, like apply
and call
, takes as its first parameter the object that this
will represent but instead of executing the function bind
returns a new function with this
already linked to the specified object, let's look at the following example:
Here we see from the same house
function two new functions were created through the use of bind
, using in each case different objects to represent this
, note how in the bind
call the house
function is not executed at any time, in this way at the end we have created a house for the Targaryen and a house for the Tyrell.
new
Binding
To understand the new binding we must know what happens when a function is called preceded by new
, in this case the following occurs:
- A new object is created.
- The new object is linked to the prototype of the function executed.
- The new object created is set as
this
within that function. - Unless the function returns something different, the new object is automatically returned by the function.
For simplicity, let's completely ignore the step 2 it and let's focus on the others
Here we see how each time the function is invoked using new
, a new object is created on each call, this object is automatically returned from the King
function even though it doesn't have return statement.
Currently the vast majority of developers don't use
new
to execute functions but when invoking es6 classes, in this example a function were used because we are analyzing the behavior of this within functions , although classes are at the end functions as well 😎😉.
Arrow functions
A new way of declaring functions was introduced in ES6(arrow functions), to declare a function in this way we use the operator =>
, for example:
One of the features of this approach is that the functions alter the behavior of this
, so that it's not dynamic depending on the function's call-site, but is lexical. In a simplified way, this
within an arrow function represents the same object that it represented in the parent scope that contains the defined function, that is, the arrow function inherits this
from the enclosing scope, example:
Observe that when the timer is executed we don't lose the reference of this
pointing to HouseStark
object, which happens in case that we pass an anonymous function function(){}
to setTimeout
, the above is because we are using an arrow function as timer first argument. The parent scope in this example is defined by the printMembers
function, when executing this function from the HouseStark
object, the implicit binding is applied and this
will be the object itself, as a consequence then this
within the arrow function will be HouseStark
object so we can access to all its properties without problems.
Determining this
To know what this
represent within a function we first find the call-site of that function, remember that this depends directly on the way in which the function is executed, then we follow this steps:
- (new binding) Is the function called using
new
? If so,this
points to a new empty object built before executing the function. - (explicit binding) Is the function executed using
call
orapply
?this
points to an object explicitly specified as the first parameter of the call. - (implicit binding) Is the function executed by accessing it through an object that contain it ? In that case,
this
represent the object that contains the function as one of its properties. - (default binding) In another case
this
points to the global scope orundefined
if we are instrict mode
.
In case of arrow functions then this
will be inherited from the enclosing scope, and this in the enclosing scope is determined following the previous steps.
Conclusions
As a recap we can say that this
in JavaScript is dynamically defined at run time, depending on the call-site of a function. There are 4 different types of binding. Using arrow functions we can say that it would not have its own this
but inherits it from the enclosing scope.
Now that we have talked in detail about this
, would you know what is the problem in the code published initially 👍
For more information on the subject I highly recommend this book from Kyle Simpson
Any recommendations do not hesitate to leave your comments, thank you for read
Top comments (0)