Friday 15 August 2014

Execution Context - The Twin Towers


To recap our last post we learned that Execution Context is bundled of
  • Variable Object
  • Scope
  • this
In this post we take up the twin towers of Execution context, namely Variable object and Scope.

Variable\Activation object

When EC is being created Javascript interpreter first scans (before executing) the functions for all arguments passed, variables in the function and function declaration in the function.

It then initializes the Variable object with:
  • arguments passed, it scans the context and initializes the variable's name & its values and create a reference copy.
  • for each function in the context create a property (with name as that of function) in the function. If there are two functions with different name the latter will override the previous one.
  • for each variable declared in the context create a named property in the Variable object. Initialize these variables with undefined.If another variable with same name found, do nothing.
  • function declaration will override variable initialization but not variable declaration.
Once the variable object is defined then the interpreter starts initializing it, this happens during the execution of the function.

Lets consider a following example
         function Sample(val1,val2){

         var name = val1;
         var age = val2;
         var count=1;
        var getName = function(){
           //
        };
    
        function incrementAge(){
          //
       }
}

Sample('Hulk',999);

During the defining phase Variable object looks like
arguments:{
  0: 'Hulk',
  1: 999,
  length:2
},
val1:'Hulk',
val2:999,
name:undefined,
age:undefind,
count:undefined,
getName: undefined
incrementAge: pointer to function incrementAge()


When Sample('Hulk',999) is called then during the initializing phase

arguments:{
  0: 'Hulk',
  1: 999,
  length:2
},
val1:'Hulk',
val2:999,
name:'Hulk',
age:999,
count:1
getName: pointer to function getName()
incrementAge: pointer to function incrementAge()

So by now you are aware of how the Variable object looks like. Now lets see how we can understand one of the core concept of Javascript called Hoisting.


Hoisting

Lets look at this example:

function foo(){
if(!a){
var a=10;
}

console.log(a);
}
foo(); //10

Its easy to explain that during the defining phase Variable object will create the variable a with undefined value, thus if condition is always true and thus the value is 10. We can confirm same by

function foo(){
console.log(a); //undefined
if(!a){
var a=10;

console.log(a);//10
}
foo();

Lets consider another example

function foo(){
    var b
    function b(){}
    console.log(b);  //[Function: b]
                              //Function declaration overrides variable initialization but not variable declaration
}
foo();

However if we modify above like

function foo(){
    var b =5
    function b(){}
    console.log(b); //5
}
foo();

Thus we see that hoisting creates some interesting (hmm..) moments

Scope

Lets take a look at this picture again


Recall that Scope is the chain of Variable objects for current EC and all the parent EC. Before digging up further lets first get our self familiarize with something called as Lexical Scope.


Lexical Scope

I have to be honest understanding lexical scoping was among the most difficult concept I had to understand and what made it worst was during my exploration I came to know that C# is also Lexical scope language.




Instead of me blabbering about type of scope in programming languages let me take the snippet out from Eric Lippert's blog 

Programming languages can be broadly divided into two categories: the lexically scoped languages and the dynamically scoped languages. The difference between the two is: in a lexically scoped language, the meaning of an unqualified name can be completely determined by looking at the program text; the analysis can be done “statically”. In a dynamically scoped language the meaning of an unqualified name can change at runtime; the name analysis can only be done “dynamically”.

Take a look at following example 
function foo(){
var x=45;
var val =goo();
console.log(val());
}

function goo(){
var x =100;
return function (){return x;};
}

foo(); //100

Since variable x will be resolved statically (meaning looking at the text) even though it appears that goo() is called with the context having x as 45, it is actually bound to the EC of goo() when it was parsed.

It gets more interesting when the function is an inner one, remember that every time a function is invoked a new EC is created (read new variable object, scope & this).

This is a classical example

var myarray=[];

for (var i=0;i<5;i++){
myarray.push(
function func(){
console.log(i);
}
);
}

for(var c=0;c<myarray.length;c++){
myarray[0]();
}

This print value 5 every time! 
I will try to explain what happened here here the variable object for EC of window contains 
myarray
i
func

when we iterate and add the func to myarray, each func gets bounded with i (defined in global context). Now when we execute the func its scope  (which is scope of the window) tries to resolve i, which by the end of the iteration has already become 5.

To verify same lets use the variable i instead of c in the second loop by doing that we are re-initializing the variable i and the value will be printed accordingly.

var myarray=[];

for (var i=0;i<5;i++){
myarray.push(
function func(){
console.log(i);
}
);
}

for(var i=10;i<15;i++){
myarray[0]();
}

//Output
10
11
12
13
14

There is very easy way to resolve/avoid above scenario but we will take that up in future posts (hint: IIFE)

Identifier Resolution / Scope Chain

Having understood the variable object and scope its fairly easy to understand how the identifiers are resolved. Just remember

Scope chain  = [Current variable object] + [variable object of parent EC's]

Inside functions scope is represented by [[scope]]  property.When an identifier is encountered, the execution context’s scope chain is searched for an identifier with a matching name. The search begins at the first object in the scope chain, the function’s variable object, and continues towards the global object until the variable is found (or ends in an error if the variable is never found)


var outer = "outer";
function oh(){
var oh="oh";
my();

function my(){
var my="my";
God();

function God(){
var God ="God";
console.log(outer + ' ' + oh + ' ' + my + ' ' + God);
}
}
}

oh();

Scope of God = Variable object for God + Variable object for my +  variable object of oh + global Variable object.


Lets take another example

function setup(items){
var divs = document.getElementsByTagName('div');
var images = document.getElementsByTagName('images');
var button = document.getElementsByTagName('save-btn');

for(var i=0;i<items.length;i++){
process(items[i],divs[i]);
}

button.addEventListener('click',function(event){
console.log('saved');
},false);
}

Here [[scope]] of the function setup contains:
[0]: Variable object for setup, which  contains agruments &  items, variables divs, images, button & i along with this.
[1]: variable object for global object

Thus to resolve an identifier the engine will start from variable object and if not found move to Global object. 

Performance consideration: Using with and catch pushes another Variable object onto the top of scope chain stack and thus adds one more look up.

This concludes our introduction to variable (activation) objects and scope (chaining). Why introduction  one may ask, well to start the implication of these concepts is a learning process (for example Closures) and it does takes some time for these concepts to settle down.

No comments:

Post a Comment