One of the most confusing and misused keyword in javascript is this
keyword.For a javascript beginner, this
feels like shrouded in mystery.But if properly used, this
can be proved as a valuable asset in the toolbox of a javascript developer.Here I would like to shed some light on what this
actually is and techniques to manipulate this
in a javascript application.The concept might appear too difficult to wrap you head around,but if you give effort and practice the concept by writing small code you will get the hang of it eventually.You could just follow along and try out the code provided in the discussion in your browser.
Any javascript programmer using this
without any prior knowledge of the outcome of the code may caught up in a situation which is hard to debug, confusing and very difficult to fix. To understand this
one must
have clear understanding of another beautiful concept of javascript which is called scope
. Before getting into how this
works and how we can manipulate it, I would like to discuss a bit about the concept of
scope.
We all know global variables, right? These are the variables which can be accessed from anywhere in your javascript code.A variable's visibility depends on its scope. Global variables can be accessed from global scope and function scope (Local Scope) equally. Chances are that you might not be familier with the concept of scope. So,I would like to discuss here about the concept of scope in a nutshell.
Simply put, Scope controls the visibility of a variable. Variables declared in global scope , has global visibility.They are accessible from anywhere in the script.On the other hand local variable resides in local scope (alos known as function scope). Visibility of local variable is limited to local scope. Generally Outside environment can not access a local variable. Of course there are some ways to interact with local variables from outside environment, But that discussion is for later.For now let's just stick to the fact that local variables are confined to local scope. They are only available in local scope. So we can say, javascript deals with two different kinds of scopes,They are :
- Global Scope
- Function Scope (Local Scope)
Here is a visual illustration for the secret world of javascript. Following illustration will provide a better understanding on relationship between variables and scope in javascript.
//javascript world
--------------------------------------------------------------------------------
- -
- //Global Scope -
- //Declare Global Variable Here in Global Scope -
- var global_var = "global variable" //here global_var is a global variable -
- -
- function sample_js_function(){ -
- -
- var local_var = "local variable" // it's a local variable -
- -
- /* -
- function scope resides inside a function -
- inside a function we can declare local variable -
- Local variables can't be accessed from outside of the function -
- scope but we can access any global variable from here -
- global_var can be accessed within `this` function -
- */ -
- -
- } -
- -
--------------------------------------------------------------------------------
Above illustration explains that global variable can be accessed from both in global scope (obviously, as they are created in global scope) and Local scope(Within any function).
keep in mind that this
refers to global object (window object) by default.Any global variable becomes a property of global object.We can access any global variable through this
keyword.this
is analogas to window object. If we check strict equality between this
and window , it will return true
this === window // true
Let's justify what i've just said with an example.For this
, we need to create a global variable first. Let's create one
var global_var = "a global variable"
If we want to see what value our global_var variable holds, we can access it directly by the variable name
console.log(global_var) // prints "a global variable"
On the other hand, as we already know global variables are added to global/window object as property,we can also access the global_var
variable through global object or window object
console.log(window.global_var) // access global_var through window object, prints "a global variable"
&
console.log(`this`.global_var) // access global_var thorugh `this`. So, `this` === window
both works equally.
What you have just learned about scope will help you to understand this
with more clarity. As I've already mentioned,this
refers to the EXECUTION CONTEXT. Execution context is a topic for another discussion. What you need to know is,execution context indicates which object is bound to the current scope.
If it feels confusing, don't worry. Following example will clarify your confusion. If you pay close attention, you will apprehend the general idea about execution context and why it is responsible for determining what this
will be pointing to during javascript execution.
If you go through the illustration I've given while discussing scope, you will see that inside javascript world there are two different kinds of scope ,one is global and another is local. When javascript begin executing the code, it first enters into the global context. Global context is window. So in global context(or I can say global scope), this
refers to window.
Concept of scope and context are related to each other.Let's explain the relation between scope and context First :
- At the beginning of execution,javascript engine enters into global scope.So,execution context is global.So this
will point to global object.
- When we invoke any function ,javascript engine enters into that function's local scope.But Execution context will still be global, so this
will still point to global object inside that function's local scope.
- If we bind the function with an object other than global object, context will refer to that bound object inside the function's private scope instead of global object.So this
will now become that particular object.
- After function execution is over,javascript engine jumps out of the function and land into global scope again.So this
will refer to global/window object again.
Let's explain the five points stated above with an example.
console.log(`this`) // here `this` refers to global/window
var person = {
username : "zami",
sayName : function(){
console.log(`this`) // here `this` will be person
console.log(`this`.username) //prints the value of username property
function inner(){
console.log(`this`) // here `this` will be window or global/window object
}
inner() // invoke inner function
}
}
person.sayName() // invoke sayName
/*
Now execution context will jumps back into global context right after
finishing function execution.
As execution is out of a function's private scope, it will again
point to global object
*/
console.log(`this`)
What's going on here? Let's explain step by step.Here I have a person
object.It has a property called username
and a method called sayName
. Inside sayName
there
is another function called inner
.
At the beginning of execution, javascript engine enters into the global context. That's why this
refers to global object inside global scope.
console.log(`this`) // so, it will pint window object in the console
Then, it found an object called person and maps it into memory.Then it finds an invocation of sayName method of the person object.
person.sayName() // it invokes sayName method and set `this` inside the function to person object
That implies why the first console.log(
this)
inside sayName method prints person
object in the console.Because as we invoked the sayName method under the context of
person object, this
will not set to person
object. That makes sense why putting a console.log(
this.username)
inside sayName method will print the value of username property
of person object.
Now execution comes to the invocation of inner
function. The point is, when javascript engine maps internal variables and function of an object to memeory, it doesn't bind
the context object to the functions that are declared inside a particular object method. That means person object is not boudn with the inner function. As a result inner function
fall subject to default binding which is global object.Thats why console.log(
this)
inside inner
function prints window in the console. So, this
refers to the global/window
object inside inner
function.
I suppose you already get the idea that this
can refer to a default value which is global object or it can be bound to a particular javascript object.There are two ways to bind this
to a particular object in javascript.
Implicit binding of this
happens when we invoke a method of an object or create a new instance of a class.
var person = {
username : "zami",
sayName : function(){
console.log(`this`)
}
}
person.sayName() // invoke sayName method of person object
Here we have an object literal called person.When we invoke the sayName method of person object, this
automatically gets bound to that object on which the method is defined. In our
case it gets bound to person object.
Implicit binding also takes place when we create new instance of a class with new keyword.Let's see this
in action with another example
// first declare a constructor function which will act as a boiler plate for our newly created instance
function Person(){
`this`.username = "zami";
`this`.sayName = function (){
console.log(`this`);
}
// create an instance of person constructor
var p = new Person()
/*
it will create an object under the hood call p.
p object will have a property called username and a method called sayName
*/
p.sayName()
/*
will print the p object in the console
`this` will be set to p object here
*/
// let's create another instance of person constructor
p2 = new Person()
p2.sayName() // It will print p2 object in the console, `this` will be set to p2 object
Explicit binding is required when we want to manipulate the this
value. Sometimes we need this
to be set to an object of our own choosing.Fortunately javascript has some built in
ways to manipulate this
value. Let's explore them briefly
call
is method which helps to set this
value inside a function. Let's see an example
var person = {
username : "zami"
}
function Shout(greetings){
console.log(greetings+" "+`this`.username)
}
Shout() // prints undefined
Shout.call(person,"welcome!") // prints "welcome! zami"
Now let's take a closer look. Here an object literal called person
is declared. It has only one property called username
.We also defined a function called Shout
which logs
this`.username`. As you already know `this` inside a function by default points to window/global object. That's why invoking `Shout` in global context prints undefined in the console. Because as you might have guessed, `this` inside `Shout` is global.Thus it's trying to find a global variable called `username` but fails. Thus prints undefined in the console. But if we use call method during invocation of Shout function and pass person object as a parameter, it binds the `this` inside Shout to person object instead of global object. That's why it successfully prints the value of `person.username` as
this.username
int the console.
apply
is a built in javascript method which does the same thing as call
. Only difference being that in case of call
we need to send the function parameters by separating them by
comma and for apply
we need to pass all the required parameters in an array.Let's see an example
var person = {
username : "zami"
}
function Shout(greetings,message){
console.log(greetings+" "+`this`.username+","+message)
}
Shout.apply(perosn,["hello","you are now a member of our football team."])
/*
first parameters is the context object which is person
Here Shout requires two parameters
That's why second parameter is an array containg two stirng value required for the parameters of Shout function
*/
bind
is also a method to bind this
to a particular context rather than the default one. bind
is completely different for both call
and apply
. Unlike call
and apply
, bind
doesn't
invoke the function, rather it returns a completely new function with this
value set to a context of our own choosing.Following example will shed some light on the concept
var person = {
username : "zami"
}
function Shout(){
console.log(`this`.username)
}
var shout_two = Shout.bind(person)
shout_two() // prints "zami"
Here we would like to bind this
inside Shout
to the person object. For this
we have used bind
method.
var shout_two = Shout.bind(person)
what the avove piece of code is doing is, it binds this
inside Shout function to person and returns a new function which gets assigned to shout_two
variable. Now shout_two
is a new function which
is exactly similar to Shout
function , only difference being that this
inside shout_two function is set to person object. That's the reason why after invocking
shout_two we get the value of username
property of person
object.
In order to write better and modular javascript code, understanding the concept of this
is a prerequisite. Incomplete or improper
understanding may lead to many unresolved and misunderstood bugs in the code. Some common errors which may appear in your code due to incomplete understanding of this
keyword will be discussed in this
topic
How we can create global variable unknowingly due to lack of proper understanding of this
keyword? We are all familiar with constructor function in javascript. They are used to create new object.General layout of a constructor function is
function Person(){
`this`.username = "zami"
}
just keep in mind that if no other execution context is provided, this
inside a function refers to global object.It is a default behaviour of this
keyword.
Try to think what might happen if we invoke Person function. A global variable called username can be created on the fly when javascript interpreter hit this`.username = "zami"` expression inside Person function.Besically what's happening here is javascript interpreter is looking for a property called `username` in
this/global
object. If such property not found in global object ,it creates one and assign value to it. On the other hand if such a property already exists, it just assign value to the existing one.
If we invoke the Person function , a global variable called username will be created and "zami"
string will be assigned to that username variable.In the mean time a property called username will be added to global object as i've stated earlier.
function Person(){
`this`.username = "zami";
}
Person()
//a property called username is added to global object
//now username variable can be accessed from global scope
console.log(username) // prints "zami"
// as a property called username is added to global object, we can access it thourgh `this` or window
console.log(`this`.username) // also prints "zami"
console.log(window.username) // also prints "zami"
Thus using this
, we can create global variable on the fly if we don't have proper understanding of this
keyword.
It is tempting to use this
as a mean of accessing local variable inside a function. Consider the following piece of code
function Person(){
var username = "zami"
console.log(`this`.username)
}
Person() // prints undefined
here username is a local variable declared inside a function called Person. Variable username is not available in global scope as it belongs to the local scope of Person function. Someone may find it provocative to use this
keyword to access the username variable inside the function scope. Here, after invocation you will see undefined
in the console.
console.log(`this`.username) //will yield undefined
As you might guess it correctly , this`.username` will look for a global variable called `username`.As there is no global variable called `username` declared , it will yield undefined.By no means
this.username
will refer to the local username variable because this
refers to global object.
Whoever is executing the task is called context. In other words, what this
refers to is called context. If we invoke a method of an object litereal, that particular object becomes the context. Context plays an important role in determining what this
will be pointing to. Let's clarify what i have just said with an example
var person = {
username : "zami",
sayName : function(){
console.log(`this`.username) // prints "zami"
}
}
person.sayName() // invoke sayName method
person
is an object literal. It has a property called username
and a method called sayName
. After invoking sayName
method it will execute console.log(
this.name)
statement. Here ``this.username
will print the `username` property of the `person` object. It is possible because execution context of `sayName` is `person` object. So, inside `sayName` , `this` will be bound to person object.
`this`.username === person.username
// we can also replace `this`.username with person.username inside sayName method
Ok, assume there is a setTimeout
function inside sayName
method. And i want to print out the username
after 5 second of invocation.
Let's rewrite the sayName method of person object.
var person = {
username = "zami",
sayName : function(){
setTimeout(function(){
console.log(`this`.username)
},5000)
}
}
//invoke the sayName method
person.sayName() // will print undefined
Inside setTimeout this
doesn't point to person
object. Because setTimeout itself is a function and context gets change for each function. Javascript
engine binds person
as the context of sayName method. But when sayName gets executed , it encounters another function which is setTimeout and when setTimeout
gets executed, context gets changed. I will left it here, perhaps I will discuss it another time as it deserves a separate article and way out of the range
of this
article.
If you are reading this
paragraph, i can hope that you have completed the whole topic. That's pretty much conclude the discussion of this
. To be honest, this
is a confusing topic. It still confuse me
from time to time. The code I've provided so far is simple and precise. Of course you will find more complex scenario in real life coding. But what I've written in this
article is the basic concept and
it will apply to any scenario in real life practice. I believe if you go through the example code and watch the results on the console and tweak them just a bit to see how the behaviour changes, you will
be able to understand any piece of code you will ever come across.