Saturday, September 8, 2012

Function/Variable Scope in JavaScript

Try to find the output of the following function -
function A(){
    this.myvar = 'a';
}
A();
alert(myvar);
var B = {
    myvar: 'B',
    C:function(){
        this.myvar = 'C';
    }
};
B.C();
alert(B.myvar);
alert(myvar);
Ans - a,C,a

once you declare this.myvar in function A, then actually we are creating a global variable. but when we are setting same value in function C within B, then actually its local value. In other words it is property of object, thus the value is set within object.
So actually function C is modifying the value of local variable named as myvar.
If we want to modify the global variable myvar within function C, then remove "this" before myvar, and directly set myvar = "C", it will modify the global variable.
To summarize- When it's an object property, the value is set within the object.

Now think about the output of this function -
function func(){
    return this;
}
alert(func()); ---- (1)

var myvar = {
    myfunc:func
}
alert(myvar.myfunc());-----(2)
In a function called directly without an explicit owner object, like case (1) causes the value of this to be the default object. case (1) is equivalent to calling window.myfunc().
In case 2 the calling object is myvar, thus the "this" object is actually myvar not window.
To Summarize - Function is a standard data type in JavaScript, an object indeed; you can pass them around and copy them. It's as if the entire function with argument list and body was copied and assigned.
In a function called using the method invocation syntax, like myvar.myfunc() or obj['myfunc'](), causes the value of this to be obj.
That's why although we declared function 'func' as global, instead of returning "window" as a value of "this" everytime, it returns it depending upon who calls it.

What if i don't want to declare the function inside this new variable, but still want to use it. Or else consider a case where a function need to be invoked on different objects. In above case , we will need to declare that function in each and every type of definition.
To overcome such situation we have "call" and "apply" method available.
We will simulate same example mentioned above to explore call and apply method.
function func(){
    return this;
}
alert(func());
var myvar = {
    name:'myname'
}
alert(func.call(myvar));
alert(func.apply(myvar));

The difference between these 2 methods comes when we pass some arguments. in case of call, we can directly pass the comma separated arguments, but in case of apply we need to pass these arguments in form of array. Here is the sample code -

function func(arg1,arg2){
    return [this,arg1,arg2];
}
var myvar = {
    name:'myname'
}
alert(func.call(myvar,'one','two'));
alert(func.apply(myvar,['one','two']));

To summarize- If we want to override the value of "this" without copying the function to another object, we can use "apply" or "call" and pass the required object in it.
NOTE- if we pass first parameter as null, then it is considered as global in javascript.

Lets try to understand Binding in JavaScript. Check the following code -
var Button = {
  click: function(){
    this.buttonclicked = true;
  }
};
var elem = document.createElement("li");
elem.onclick = Button.click;
elem.onclick();
alert(elem.buttonclicked); --> alerts true
alert(Button.buttonclicked); --> Alerts undefined

In the above example if you see, the 'buttonclicked' is the property of Button no element but still i see its available in element as well(of course accidentally).
In order to get the correct behavior we bind the Click property with Button. Check out the following code -
function bind(context, name){
  return function(){
    return context[name].apply(context, arguments);
  };
}
var Button = {
  click: function(){
    this.buttonclicked = true;
  }
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
elem.onclick();
alert(elem.buttonclicked); --> undefined
alert( Button.buttonclicked); --> true
in the above code we have bind the buttonclicked property with button.

To make it more generic its always possible to attach this bind property with function class itself.
If we define this on top of the page -
Function.prototype.bind = function(object){
  var fn = this;
  return function(){
    return fn.apply(object, arguments);
  };
};
then we can directly call - elem.onclick = Button.click.bind(Button);
because now every function has bind property associated with it.

Importance of new operator in JavaSCript-
Consider following example -
function A(){
    var name = "A";
}
var b = A();
var c = new A();
alert(typeof(b)); ----(1)
alert(typeof(c));-----(2)
In the above code b is undefined and type of c is object. In javascript new operator is used to create new object. Whenever we use "new" keyword, java script run time provides a new value of "this" object.

No comments:

Post a Comment