Introduction
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.
Let's Talk About Scope
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.
Scope determines the visibility level of a variable......
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 analogous 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.
//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 -
- */ -
- -
- } -
- -
--------------------------------------------------------------------------------
this === window // true
var global_var = "a global variable"
console.log(global_var) // prints "a global variable"
global_var
variable through global object or window objectconsole.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
What THIS is all about ?
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.
Execution context refers to the object which 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.
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
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.Execution context refers to the object which 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.
'this' refers to execution context...............
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.Now 'this' will 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 scope. That's why this refers to global object in 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 inner
function and invokes it. Here comes the tricky part. Current context of a function doesn't get bound to the functions that are declared inside that function. That means person object is not bound 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.
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.Now 'this' will 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)
person
object.It has a property called username
and a method called sayName
. Inside sayName
there is another function called inner
.console.log(this) // so, it will pint window object in the console
person.sayName() // it invokes sayName method and set THIS inside the function to person object
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.inner
function and invokes it. Here comes the tricky part. Current context of a function doesn't get bound to the functions that are declared inside that function. That means person object is not bound 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.How THIS gets bound to a particular object
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 :
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
var person = {
username : "zami",
sayName : function(){
console.log(this)
}
}
person.sayName() // invoke sayName method of person object
// 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
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
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.
call
is method which helps to set this value inside a function. Let's see an examplevar person = {
username : "zami"
}
function Shout(greetings){
console.log(greetings+" "+this.username)
}
Shout() // prints undefined
Shout.call(person,"welcome!") // prints "welcome! zami"
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
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
*/
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 examplevar 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
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 invoking shout_two we get the value of username
property of person
object.
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 conceptvar person = {
username : "zami"
}
function Shout(){
console.log(this.username)
}
var shout_two = Shout.bind(person)
shout_two() // prints "zami"
Shout
to the person object. For this we have used bind
method.var shout_two = Shout.bind(person)
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 invoking shout_two we get the value of username
property of person
object.Conclusion
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 behavior changes, you will be able to understand any piece of code you will ever come across.
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 behavior changes, you will be able to understand any piece of code you will ever come across.
No comments:
Post a Comment