What is ‘this’ keyword ?
In javascript ‘this’ is a keyword that refers to the object in the context. It points at the object whose method we are executing.
What do we mean by ‘this’ refers to object?
var person = { name: "Bruce", printName: function(){ console.log(person.name) } } person.printName() //Bruce
Here, ‘this’ keyword refers to person object, a parent object (we will discuss why it refers later) which means instead of person.name we can write this.name inside the function printName.
var person = { name: "Bruce", printName: function(){ console.log(this.name) } } person.printName() //Bruce
What is the value of ‘this’ ?
According to Spec 4.3.31 Method, ‘this’ value of function is its parent object i.e when we invoke method property of an object that method property gets that object as its ‘this’ value.
Value of ‘this’ keyword depends on
- how we invoke the function not just where we defined it (wrote it).It has nothing to do with the function
- where the function was invoked
- we don’t know what ‘this’ keyword is until the function is invoked
Let me illustrate what I mean.
function greet(){ console.log(“Hello “ + this.firstName) } var person = { firstName: “Bruce” } greet() // Hello undefined greet.call(person) // Hello Bruce
So, how are we invoking greet function here?
We are invoking greet function in two ways.
When we invoke greet() as stand alone function ,’this’’ keyword points at global object. Since there is no variable firstName with value in global scope, we get undefined.
When we invoke greet.call(person) , ‘this’ points at person object and calls the function in that context.
How can we determine what ‘this’ keyword is?
1. Global Context
When ‘this’ keyword is not inside a function i.e when ‘this’ keyword is in global execution context, its value is global object. In browser that would be window and in node module that be module.exports.
(in web browser)
console.log(this === window) //true var fruit = "apple"; console.log(this.fruit) // apple console.log(window.fruit) //apple
(Note: Every variable declare in global scope is attached to window object)
2. Implicit Binding
According to Spec 4.3.31 method, ‘this’ value of function is its parent object i.e when we invoke method property of an object that method property gets that object as its ‘this’ value. (function defined inside object is called method)
var person = { name: "Banner", change: function(){ this.name = "Hulk"; return this.name.toUpperCase(); } } person.change() //HULK
In this example, ‘this’ keyword inside change method refers to person object which is why we can access and mutate name property of person object.
This is called Implicit Binding, a function(method) invoked by dot notation.
You can also understand it this way.
person.change() // as shown in the example above
(looks something like this)
object dot function() being executed
‘this’ keyword will refer to or point to or set to anything before (left to) dot.
If there is no dot and we are just calling function, ‘this’ will be set to global object, window.
Why ‘this’ value will be global object?
When we define function in global scope, it will attach to global object, window i.e it becomes method property of window.
function globalThis(){
console.log(this === window)
}
globalThis() //true
This is same as below because globalThis function is a method property of window.
window.globalThis() //true
Since globalThis function is invoked by window object, ‘this’ keyword will point to window.
What is the benefit of Implicit binding?
function avengers(){ console.log(this.name+" is an Avenger") } var hero1 = { name: "Captain America", avenger: avengers } var hero2 = { name: "Iron Man", avenger: avengers } hero1.avenger(); //Captain America is an Avenger hero2.avenger(); //Iron Man is an Avenger
Here we have one function which is shared among two different objects.
With implicit binding, we can invoke that one function in different context each time. We don’t need to write separate function for each object.
Consider this example
var person = { firstName: "Bruce", lastName: "Banner", superHero: "Hulk", fullName: function(){ console.log(this.firstName+" "+this.lastName) }, secret:{ identity: function(){ console.log(this.firstName +" is "+ this.superHero) } } } person.fullName() //Bruce Banner person.secret.identity() //undefined is undefined
Why do you think we are getting undefined?
Here we have secret object property inside person object. It has identity method(function) which is console logging this.firstName and this.superhero.
Notice how we are invoking identity method(function).
person.secret.identity()
‘this’ keyword is set to secret object because it comes right before dot and identity method which is getting executed. Since secret object has no firstName and superHero property, we get undefined.
We can also say that the value of ‘this’ keyword will be its closest parent object. In the code example above, closest parent object of identity method is secret object, which is why ‘this’ keyword refers to secret object.
So how can we fix this? How can we get access to property of person object ?
We have to change where ‘this’ keyword points at. We have to somehow explicitly set value of ‘this’ keyword to person object.
3. Explicit Binding
In explicit binding, we set or force ‘this’ keyword to point at particular object when we invoke the function i.e we decide what the value of ‘this’ keyword is to be when calling function.
function avengers(){ console.log(this.name+" is an Avenger") } var hero1 = { name: "Captain America" } var hero2 = { name: "Iron Man" } avengers.call(hero1) // Captain America is an Avenger avengers.apply(hero2) //Iron Man is an Avenger
Notice here function avengers is not a method of both hero1 and hero2 object.
It is a stand alone function.
But whenever we are invoking it, we are setting ‘this’ keyword to refer to a particular object with call and apply function.
Call and Apply function helps to set the value of this keyword by taking the object that you want to refer to as its first argument.
avengers.call(hero1)
Here we are invoking avengers function by explicitly binding by call function that takes hero1 object as it’s first argument.
This forces the value of ‘this’ keyword to be hero1 object.
Let’s revisit our previous code.
var person = { firstName: "Bruce", lastName: "Banner", superHero: "Hulk", fullName: function(){ console.log(this.firstName+" "+ this.lastName) }, secret:{ identity:function(){ console.log(this.firstName+" is "+ this.superHero) } } } person.secret.identity() //undefined is undefined
Here we want ‘this’ keyword to point to person object when we invoke identity method of secret object.
We can use call() to explicit bind ‘this’ keyword to person object.
person.secret.identity.call(person) // Bruce is Hulk
Even though identity is a method of secret object, now the value of keyword ‘this’ is not secret object but person object.
This illustrates our previous assertion that value of ‘this’ keyword depends on how the function is invoked not where it is defined.
losing ‘this’ binding
var person = { name: 'Goku', printName: function(){ console.log(this.name) } } person.printName() //Goku function dbz(para){ for(var i=0; i<3; i++){ para(); } } dbz(person.printName) // undefined //undefined //undefined (this binding is lost) dbz(person.printName.bind(person)) // Goku // Goku // Goku
(bind helps to specify the value of ‘this’ keyword in advance. In this example, when function is invoked at each iteration we already have ‘this’ keyword pointing at person object)
bind() is similar to call() but unlike call() it does not invoke function instead it gives you new function with it’s ‘this’ keyword explicitly bound. So, when you invoke that new function, it’s ‘this’ keyword is already pointing to object specified by you.
But why did we lose this binding?
function dbz(para){ for(var i=0; i<3; i++){ para(); } }
In above code example, function is invoked at para(). Here the value of keyword this is global object.
dbz(person.printName.bind(person))
With this line of code, we hard bounded the keyword this to person object in advance. When it gets invoked, it has it’s this keyword already pointed to person object.
Helpful Tip
call, apply and bind can only be invoked on function.
4. new keyword
When you see new keyword with function invocation, it means new object is created with this keyword pointing to it (object).
function dbz(){ this.name = 'Goku'; } var person = new dbz() console.log(person.name) // Goku
(this is pointing to newly created object person)
Summary
We saw 4 ways of invoking function.
Value of keyword this is determined by how function is invoked.
Follow this binding rule precedence to determine the value of this keyword.
Is it invoked with new keyword?
Is it invoked with call(),apply() and bind()? (explicit binding)
Is it invoked with method function? (implicit binding)
Is invoked in global scope?