Much like nailing jelly to a wall, the this
keyword in JavaScript is a bit like a whack-a-mole game. Each time you think you’ve got it, you realize that this
might not be referring to what you thought it was! Once again, you find yourself trying to decipher how and what this
is doing for you in your JavaScript program. In JavaScript, there are four key rules to memorize to determine what this refers to. There is also an order of precedence to these rules. In this tutorial, we’ll examine exactly what this
does for you in JavaScript and memorize the four rules that determine its context. Let’s dig in now.
this
refers to an object
One thing we can be confident of is that no matter what, this
always is referring to an object. It will never refer to a primitive value like a boolean, string, or number. It will always be an object that it refers to.
Function Context
Before we get to the rules that determine how this
works, we first need to understand a little bit about how functions in JavaScript work. Mainly, we need to be aware of the following:
Every function, while executing, has a reference to it’s current execution context. The current execution context is referred to as the
this
keyword.In other words,
this
is what object is associated with the current function call
With this in mind, we must be mindful of the most critical thing to look for when dealing with this
in JavaScript. We are referring to how the function is called and when it is called. In other words, we must pay special attention to the call site of that function. For it is the call site that determines what this
is bound to. In essence, this
acts as a type of dynamic scoping mechanism in JavaScript.
To demonstrate this, we will create several different JavaScript objects. Each object will have a property named prop1
, and a property named decoder
. The decoder
property will have a reference to a function named thisdecoder()
on all objects. We will see that the value contained in prop1
will depend entirely on the call site of the thisdecoder()
function.
Here are the four this
binding rules in order of precedence.
All rules depend on the call site of the function! If you are ever confused about what the binding of this is referring to, then you are to find the call site of the function and ask which of the following cases the function got executed as.
1. The new
Binding
The rule that takes precedence over all others when dealing with this
, is the JavaScript new keyword. We know that when the new keyword is used with a JavaScript function call, a new object gets created out of thin air. In this scenario, the newly created object is set to this
for that function call.
function thisdecoder() {
console.log(this.prop1)
};
function vehicle(make) {
this.prop1 = make;
this.decoder = thisdecoder;
};
//---Binding via new keyword---//
acar = new vehicle('Porche');
acar.decoder();
// Porche
atruck = new vehicle('Mack');
atruck.decoder();
// Mack
2. Explicit Binding
Explicit Binding is handled with the apply()
, call()
, and bind()
methods of a function object. In JavaScript, functions are objects, therefore functions themselves can have methods. The first two methods are similar, in that the first argument provided to them is the object to which you want the this
keyword to be bound to. apply()
and call()
are almost the same thing, but the difference between them is that call()
accepts an argument list, while apply()
accepts a single array of arguments. In our example, we only pass one argument, the object we want bound to this, so in our case apply()
and call()
work identically.
bind()
is a little more confusing. It actually creates an entirely new function, which is then itself callable. When you call that new function, the this
keyword is bound to the provided argument which was given to the bind()
invocation.
function thisdecoder() {
console.log(this.prop1)
};
var prop1 = 'This string lives in the global scope.';
var objectone = {
prop1: 'This is a string in object ONE.',
decoder: thisdecoder
};
var objecttwo = {
prop1: 'This is a string in object TWO.',
decoder: thisdecoder
};
var objectfour = {
prop1: 'This is a string in object FOUR.',
decoder: thisdecoder
}
var objectthree = {
prop1: 'This is a string in object THREE.',
decoder: thisdecoder
};
//---Explicit Binding---//
objectone.decoder.apply(objectthree);
// This is a string in object THREE.
objecttwo.decoder.call(objectone);
// This is a string in object ONE.
var bound = objectthree.decoder.bind(objecttwo);
bound();
// This is a string in object TWO.
3. Implicit Binding
Implicit binding is pretty easy. Implicit binding happens when you call a function as a property or method of a given object. In our example below, all objects have a decoder
property. Furthermore, each of these properties simply reference the same function called thisdecoder()
. None of the given objects owns the thisdecoder()
function any more than the other, they all simply have peer references to the same function. You could say we have put a reference to a function on an object. The implicit binding rules states that the object at the call site, also known as the base object, context object, or containing object, becomes the binding for the this
keyword.
function thisdecoder() {
console.log(this.prop1)
};
var prop1 = 'This string lives in the global scope.';
var objectone = {
prop1: 'This is a string in object ONE.',
decoder: thisdecoder
};
var objecttwo = {
prop1: 'This is a string in object TWO.',
decoder: thisdecoder
};
var objectfour = {
prop1: 'This is a string in object FOUR.',
decoder: thisdecoder
}
var objectthree = {
prop1: 'This is a string in object THREE.',
decoder: thisdecoder
};
//---Implicit Binding---//
objectfour.decoder();
// This is a string in object FOUR.
objectthree.decoder();
// This is a string in object THREE.
objecttwo.decoder();
// This is a string in object TWO.
objectone.decoder();
// This is a string in object ONE.
4. Default Binding Rule
Finally, we come to the default binding rule. The default binding rule applies when you call a function, and none of the prior three scenarios applies. It looks like a plain vanilla function call. When this is the case, this
gets bound to the global object. In the browser, this is the Window object. note: You may be spontaneously roundhouse drop kicked by the the force of Douglas Crockford if you use this approach. We’re kidding of course. In all seriousness however, this binding rule has created many a security risk and headaches for JavaScript developers. To mitigate this, you can use strict mode in your JavaScript code, and the this
binding will default to undefined
rather than the global object.
function thisdecoder() {
console.log(this.prop1)
};
var prop1 = 'This string lives in the global scope.';
var objectone = {
prop1: 'This is a string in object ONE.',
decoder: thisdecoder
};
var objecttwo = {
prop1: 'This is a string in object TWO.',
decoder: thisdecoder
};
var objectfour = {
prop1: 'This is a string in object FOUR.',
decoder: thisdecoder
}
var objectthree = {
prop1: 'This is a string in object THREE.',
decoder: thisdecoder
};
//---Default Binding---//
thisdecoder();
// This string lives in the global scope.
What Does this Refer to in JavaScript Summary
In summary, it doesn’t matter where a function in JavaScript is declared. Nobody owns a function more than anybody else in JavaScript, everything is simply a reference. The only thing that matters with regard to the this
binding in JavaScript is what the function call site looks like.
-
1.
new
keyword bindingnew vehicle();
-
2. Explicit binding with
apply()
,call()
, orbind()
objectone.decoder.apply(objectthree); objecttwo.decoder.call(objectone); var bound = objectthree.decoder.bind(objecttwo); bound();
-
3. Implicit Binding
objectfour.decoder(); objectthree.decoder(); objecttwo.decoder(); objectone.decoder();
-
4. The default binding rule
thisdecoder();
These rules are a must for you to memorize in order to make your life easier when dealing with this
bindings in JavaScript!