Wednesday 6 September 2017

Javascript THIS


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.

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.

'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.

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

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.

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
*/

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 applybind 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.

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.

No comments:

Post a Comment

Alternative to Undefined in Javascript

you might notice something strange in the title. It's doesn't make sense,right?  undefined  is  undefined . What does it have t...