When I am learning JS, the mythical this keyword has caused me a lot of trouble. But once I got my head around it, the idea seems simple, simple enough for me that, all I had to do now was to question myself about the this and I can understand how it will be. So let's try to demystify this annoying this.
In a way, it's easy to understand this if you can wrap your head around function invocation. This is not exactly how it works behind the scenes, but our discussion and for learning, let's dumb it down way down to my level and talk. So consider that whenever you call a function or invoke a function (note that defining a function and calling it are 2 different things), we can think of the function's call method is called. And the call method is simple, it has 2 arguments, first is a thisValue that is taken as the this and the second is an argument list made of all the arguments for the function. So in code, it will be like,
function hi(arg) {
console.log(this + " hello to " + arg);
}
//When we call it like this
hi('Readers')
// Accoding to our discussion it becomes,
hi.call("thisValue", "Readers") //=> thisValue hello to Readers
So as we discussed, the this value was changed to whatever I passed in as the first argument.
Now let's see what really happens :
function hi(arg) {
console.log("Hello to my " + arg);
}
//When we call it like this
hi('Readers')
// What actually happens
hi.call(window, "Reader") //=> Hello to my Readers
So when you think about it, calling a function with call always can get annoying, so the language does that for us, like shown above, by attaching the this value at the call site to the function. Remember, it attaches the this value of the call-site. This whole thing can be called default binding.
Now let's check another scenario where we call a function defined on an object. For example:
let obj = {
name: "Sebu Elias",
hiAgain: function (arg) {
console.log(this.name + " welcomes "+ arg);
}
}
// So when we call
obj.hiAgain("reader")
// It is called like,
obj.hiAgain.call(obj, "reader")
So when you look at it, you can see the this attached to the function call is no longer related to the call site, but the object it was called through. You can test the above code by calling the hiAgain without obj like hiAgain()
, then the this.name will throw an error, as the this of the call is window, like we discussed early, as it is default binding. The binding that we saw here when calling through the object is called implicit binding.
Now it is important to note that functions do not have a persistent notion of it's this. It is always set at call time based on the way it was invoked by the caller.
But sometimes it is very useful to have the this of a function to be persistent to a certain value, and for this we can use closure that we learned before.Taking the above example:
let obj = {
name: "Sebu Elias",
hiAgain: function (arg) {
console.log(this.name + " welcomes "+ arg);
}
}
let bindingThisTohiAgain = function(arg) {
return obj.hiAgain.call(obj, arg);
}
bindingThisTohiAgain('reader');
In this cases, even if the bindingThisTohiAgain("reader")
breaks down to bindingThisTohiAgain.call(window, "reader")
, internally we change the this right back in our call method.
This seems like a very good feature and very useful one, so useful that we can try make it a general purpose one :
var bind = function(func, thisValue) {
return function() {
return func.apply(thisValue, arguments);
}
}
let bindingThisTohiAgain = bind(obj.hiAgain, obj);
bindingThisTohiAgain("reader"); //=> "Sebu Elias welcomes reader"
So, the first arguments
is an array of all the remaining arguments passed to a function. Second, you must have noted that we used apply here instead of call, they both work exactly the same, just that apply, for its second argument, takes an array instead of arguments one by one like in call.
So it turns out what we did above was so much useful that, they added it as a feature to the language itself, by adding the bind
function.
So we can do,
let bindingThisTohiAgain = obj.hiAgain.bind(obj);
bindingThisTohiAgain("reader");
This is a type of binding is called forced binding. It can be especially useful for callbacks. When we think about it, the callbacks get called somewhere else entirely, as we are only passing the function reference, and we have no control over its invocation at all. So it is very important to have control over the this of a function passed for a callback.
Another method of bind is there called explicit binding, which we have seen throughout this article. It is those cases where we bind the this using call
or apply
methods.
We can say there is a sort of binding left to discuss, that is the binding using the keyword new, that in itself is a fairly large topic, which I will try and cover, but this has gotten long enough I think.
So I hope that with this idea, you will be able to recognize the this s' of the JS world and when you ask yourself, what is the this of a function, now you can confidently answer and understand where the this needs to be bound and why it is bound.