Javascript this keyword has been always a source of trouble for developers, they are confused about how this value is resolved.
If you are one of these developers don't worry, this article will make it easier to understand how this mechanism works.
This Binding follows four main rules (Yes all this headache is about four rules) and a lexical this(arrow function) :
- Default binding
- Implicit binding
- Explicit binding
- New binding
Lexical this (has its own rule)
Let's see in details what are these four rules in details
1. Implicit binding
Given a function inside an object property, implicit binding says that the this for the function when it is called is the object itself.
function write(){
console.log(this.a)
}
var obj={
a:2,
write:write
}
obj.write()//2
In other words, in obj.write(), we are saying implicitly this = obj, therefore, this.a == obj.a.
2. Explicit binding
Is "forcing" this to take a specific value and this through apply(), call() and bind() functions. They take as first parameter an object to use as this value and a second one representing the function argument(s).
function write() {
console.log(this.name);
}
write.call({ name: "HSSAN" }); // HSSAN
In this example, this = {name:"HSSAN"}, therefore, this.name == "HSSAN"
3. New binding
In my article Javascript classes: class, constructor, new, extends, super I discussed how new keyword works under the hood. Briefly, it creates an object, and this is bound to this newly created object, then, it gets returned.
function write(a){
this.a=a
}
var writing=new write("hello");
console.log(writing.a);//hello
4. Default binding
When all the previous rules do not apply, this is bound to the global object. In strict mode it is bound to undefined.
Lexical this (arrow function)
Arrow function has a different approach of how to deal with this binding. In arrow function this is determined by lexical scope, in other words, where the function is declared determines this binding (enclosing scope where it inherits from).
this.name = "HSSAN";
this.skills = ["shoot"];
var player = {
name: "Kane",
skills: ["dribble", "pass", "shoot"],
showSkills: () => { // this here is lexically inherited from player
this.skills.forEach((skill) => {
console.log(`${this.name} can ${skill}`);
});
},
};
player.showSkills(); //HSSAN can shoot
showSkills is an arrow function, so it inherits this from enclosing scope. Enclosing scope, in this case, is player object, which has global object as scope. Therefore our function will inherit global object for this binding.
What order to apply the rules when more of one is eligible?
We are going to demonstrate with concrete examples which rule has more precedence than others.
Explicit VS Implicit
var player={
name:"Kane",
skills:["dribble","pass","shoot"],
showSkills:function(){
this.skills.forEach((skill)=>{
console.log(`${this.name} can ${skill}`)
})
}
}
var func=player.showSkills.bind({name:"bale",skills:["shoot"]})
func()//bale can shoot
func() returns 'bale can shoot' so player binding gets ignored(implicit binding) to use instead explicit binding (In the example we used bind but it can be replaced also by call or apply).
With this simple example, we can say explicit binding has more precedence than implicit binding.
New VS Implicit
function getName(name)
{
this.x=name
}
const obj={name:getName}
const obj1 = new obj.name("Hssan");
console.log(obj.x)//undefined
console.log(obj1.x)//hssan
obj.x has undefined value, in other words, x attribute did not get created in obj object, instead, a new object is created with x attribute (obj1 object). If we remove new keyword, the opposite will happen obj.x="hssan" and obj1 become undefined.
Accordingly, new binding has more precedence than implicit binding.
New VS Explicit
function getName(name){
this.name=name}
var obj={}
var func=getName.bind(obj);
func("Hssan");
console.log(obj.name); //Hssan
const n=new getName("Bale");
console.log(n.name); //Bale
console.log(obj.name); //Hssan
In this demonstration, func is hard-bound against obj that's why obj.name has "hssan" value and new getName("Bale") did not change obj value, instead it creates and returns a new object with attribute name="Bale".
new has the ability to override hard-bound since it returns the new created object (n). For that reason, new Binding has more precedence than explicit binding.
Conclusion:
we have seen together different rules applied to know which value this is binding for.
Now let's summarize their order of precedence :
1) Is the function called with new ? if so, this is the object created by the function
2) Is the function called with call,apply or bind? this refers to the object passed in the argument
3) Is the function called with contextual object (object owner) ? this refer to the contextual object.
Hope this quick article makes this binding in javascript clearer and no longer a nightmare.
Top comments (1)
Somehow never liked classes and 'this' keyword 😑 Switched to hooks like the next day they released v16.8 😀😀