Grasping Javascript: this Keyword
this
can be pretty hard to understand when first learning Javascript. Hopefully this article can help in grasping this
a little easier (pun intended). If you prefer to see the code blocks with text instead of pressing links go here.
Looking for the Call-Site
It is best to understand where this
is a reference to by looking at where a function is called. Emphasis on where the function is called and not where it is declared. We are looking for the call-site because it's the only thing that matters for this
binding.
Let’s look at an example of what call-sites are: https://gist.github.com/5cc762b79099e95cc295d32e6adbb8d4
Knowing the call-site allows us to better understand where this
is binding to.
The 4 Rules
There are four kinds of rules that help what this
binds to. Utilizing these four rules when insepcting call-sites will help in better understanding this
. The four rules are default binding, implicit binding, explicit binding, and new binding. We'll go into depth on these rules.
1. Default Binding
The most common call-site is the default binding and to be referred to when none of the other rules apply.
Example: https://gist.github.com/b7d8341af130e93d54fabc6d7e9274f5
The first thing to note is the global-scope. Here, variable word
is in the global scope and, therefore, the global object property is word = 'hello'
. The second thing to note is to look at the call-site, foo()
. It's just an ordinary reference for the function foo()
and this.word
would resolve to that call, which points to the global object.
2. Implicit Binding
The second rule to remember is if the call site is in the context of an object.
Example: https://gist.github.com/3198565d236804817a0ada145777b9d6
There are a few different things to notice in the example above. The first is how sayName()
is referenced on the obj
object and at the call-site sayName()
is called as a property for obj
. So in a sense the sayName()
function is "owned" by obj
. Therefore, from the implicit binding rule, the this
keyword points and binds to the obj
object. The reason why I quoted "owned" is because of some problems that can happen if implicit ruling is applied.
A frustration that comes with implicit binding is how an object can lose its this
binding. The default rule is used when the implicit rule loses its binding, which then binds to the global object or is undefined.
Example: https://gist.github.com/229c34a087c7a51d882e2ab9a0d4a2c2
In the example, it may seem bar()
should say "Adrian" since it looks like it would get the same value as obj.sayName
but bar()
is just another reference to sayName()
. bar
is just getting a reference to obj.sayName
but not invoking it. What matters is how the call-site looks like. bar()
is just an undecorated function call so default binding is in effect and obj.sayName
when declaring bar
just a reference.
3. Explicit Binding
Explicit binding can happen by invoking a function’s apply()
or call()
method. Explicit binding allows you to not add the function as a property to the selected object. Instead, call()
or apply()
takes the selected object as its parameter and invokes the function on the specified object.
Example: https://gist.github.com/6a22e1e276750807a1993bfc59074b0e
In this example, this
in sayName()
explicitly binds to obj. Explicit binding allows you to force this
binding to the object in call/apply's parameter. Note that call()
and apply()
are similar but differ in their additional parameters.
Unfortunately, explicit binding alone doesn’t rid us of the perils of a function “losing” its this
binding. Functions can still "lose" its binding or even be paved over by frameworks.
Enter hard binding, a variation of explicit binding which helps us get around losing the this
binding.
Example: https://gist.github.com/79b94a359eea5c0a78bb012b95a35dd8
Using the bind() method will now point this
to the specified obj
placed in its parameter.
4. new
Binding
The last rule for this
binding is the new
binding. Although the keyword new
is familiar to those who already know class oriented languages, it is not exactly the same. Javascript's "constructor" functions are not the same because they do not belong to any classes and are not instantiating a new class. Let's look at an example.
https://gist.github.com/70027e04a334c8fa633f52befd9d5754
When new
is placed in front of a function a new
object is created, that object is prototype linked, and this
binding is pointing to that object.
Which Has Priority
Let’s review the four rules and apply them to see which rule will take precedence over the other.
https://gist.github.com/c6ef9fbaf3f108cc90ff07fa0e7ea2bb
In the case above, explicit binding is precedent over implicit binding.
Now let’s see new
binding's precedence in what we have so far.
https://gist.github.com/c128beed42015440fa7094bfe78712bf
In the example above new
binding takes precedence over implicit binding.
Let’s now see if new
binding takes precedence over explicit binding.
https://gist.github.com/8a14eb0ceada9df20410000513a80b32
We notice new
binding didn't change person.name
's value but the new
binding created a new object who's name property is "bob". Although we bound person
to name
with new
, bob.name
is "bob".
In summary, there are four questions to ask when figuring out the value of this
.
- Is the function called with
new
binding? If yes,this
value is the newly created object.
https://gist.github.com/1c5aa31acb8787871bbf12bae5cf81a4
- Is the function called with
call
orapply
(explicit binding) or hard bound withbind
? If yes,this
value is the explicitly called object.
https://gist.github.com/a8ced5c4e52d30161b18687f2126f9bb
- Is the function called in the context of an object or is “owned” by one?
this
value is the context object.
https://gist.github.com/d57b7a968d6460061994545ed75265aa
- If all else fails then default binding is the answer and
this
is the global object.
Those are the 4 rules that help illuminate what this
value is.
I hope reading this article helps in understanding this
a little more.
If you found it helpful don't forget to leave a few claps and follow me on twitter.
Originally published at gist.github.com.