These are a few key concepts of JavaScript language that developers should know: execution context, activation object, variable instantiation, scoping, closure, eval and “this” keyword. Knowing these would help one tremendously in Ajax development.
For example, when you write an inner function, you know that you can access the local variables defined in the outer function as if they were defined locally. You can also access the global variables. -Why? How does the host environment resolve such variables?
Another example: When you pass arguments to a function, you can access these arguments as if they were locally defined variables. How does this work?
A slightly more involved example that developers must have seem similar code a lot but may not know the “why”:
function outerFunc(outerArg){
var localVar = 100;
function innerFunc(innerArg){
localVar+=100;
return (outerArg +innerArg + localVar);
}
return innerFunc;
}
var globalVar = outerFunc(200);
alert(globalVar(300)); //this displays 700
alert(globalVar(300)); //this displays 800
globalVar = outerFunc(200)
alert(globalVar(300)); //this displays 700 again
Why would the 2nd alert box show 800 (means the value of localVar is retained from the first call) while the third alert displays 700 (the value of localVar is not retained from previous calls)? How does it work?
All JavaScript code executes in an execution context. Global code (code executed inline, normally as a JS file, or HTML page, loads) gets executed in a global execution context (in a browser, the global context is typically the window object), and each invocation of a function (possibly as a constructor) has an associated execution context. Code executed with the eval and setTimeout functions also gets a distinct execution context.
When a JavaScript function is called, it enters an execution context. If another function is called (or the same function recursively), a new execution context is created and execution enters that context for the duration of the function call. When that called function returns, control returns to the original execution context and the just finished execution context is available for garbage collection except for cases like closure. Thus running JavaScript code forms a stack of execution contexts.
The way that variable instantiation works is to instantiate each of the function’s formal parameters, local variables and inner function declarations as named properties of the Variable Object. In the process, named properties of the Variable object are created for each of the function’s formal parameters. If arguments to the function call correspond with those parameters, the values of those arguments are assigned to the properties (otherwise the assigned value is undefined). If there is an inner function declaration, a function object is created. This function object is assigned a property of the Variable object with name corresponding to the function name used in the function declaration. The last stage of variable instantiation is to create named properties of the Variable object that correspond with all the local variables declared within the function.
The properties created on the Variable object that correspond with declared local variables are initially assigned undefined values during variable instantiation, the actual initialization of local variables does not happen until the evaluation of the corresponding assignment expressions during the execution of the function body code.
However, it is very important to point out that the Activation object is used as the Variable object. Though conceptually they are called out as different objects, they are the same object in reality. This is important as you can see quickly in “scope and scope chain” and “variable resolution” later on.
It is the fact that the Activation object, with its arguments property, and the Variable object, with named properties corresponding with function local variables, are the same object, that allows the identifier arguments to be treated as if it were a function local variable.3. Scope, Scope Chain and Variable Resolution:
Each execution context has an assigned scope when created. A scope can be thought of as an “object” that is associated with a particular execution context (this scope object is actually the Activation Object we mentioned above). A scope chain consists of a list (or chain) of such objects. The scope chain of an execution context is a chain of objects with the current “scope” object on the top, the “scope” object of the calling execution context as the 2nd object on the top, and so on, with the last “scope” object of the chain being that of the global execution context.
What do scope and scope chain do? They are used for variable resolution. When some JavaScript refers to a variable "foo", the host environment will try to resolve this variable. This resolution process is against the scope chain of the current execution context. First, it will search to see if there is a variable with the same name in the current scope. This is done by checking to see if the current “scope” object has such a named property. Remember that we mentioned Activation Object and Variable Object are the same object, and local variables (including inner function declarations, and function arguments) are instantiated as named properties of the Variable object during variable instantiation. So if the “foo” variable is defined in the local execution context, there must be a property of the Variable object named “foo”. If such a named property is not found, it means this “foo” variable is not defined here. The resolution process will now go down the scope chain and check against the 2nd “scope” object and so on until such such a named property is found or otherwise an “undefined” error happens
(BTW: Prototype chain is always part of property resolution process and no difference here either. The normal prototype chain lookup process applies when looking for a property with a specific name from a “scope” object: If no such property is found on the object itself, the prototype object will be searched. Still not found, the prototype of the prototype object will be searched. Until either the property is found or the end of the prototype chain is reached).
Important to point out: each function object has an internal property called [[scope]] (see more info on this later in this post) that consists of a list (or chain) of objects. This [[scope]] property can not be directly referenced by JavaScript code. When the function object is created, its internal [[scope]] property is assigned to the execution context in which this function object is created. This execution context has its own scope object and so on. In the end, the [[scope]] property is associated with a scope chain with the execution context in which the function object is created being on the top.
The scope chain of the execution context of a function call consists of the list referred to by the [[scope]] property of the corresponding function object and the Activation object created for this function call. The activation object is at the front of the chain (or the top of the list).
This would normally be the case upon exiting an execution context. Any objects created within the exited execution context, including function objects, would no longer be accessible and so would become available for garbage collection. However, Closure is the exception.
A closure is an expression (typically a function) that can have free variables together with an environment that binds those variables (that “closes” the expression). A closure is formed by returning a function object that was created within an execution context of a function call and assigning an accessible reference to that inner function. The accessible reference can be established by directly assigning the returned function object to a global variable, a property of a globally accessible object or an object passed by reference as an argument to the outer function call, etc.
In the code example above, line “var globalVar = outerFunc(200);” calls the outer function "outerFunc". This call returns an inner function object and this returned inner function object is assigned to a global variable called globalVar. As a result, a closure is formed here.
The key concepts of execution context can be summarized using the following illustrations (assuming the function is named "foo"):
The Execution Context of a call to function "foo":
. Activation object
. Variable object
. Scope chain
The [[scope]] property of function object “foo” is:
foo.scope:
= the scope chain of the execution context where "foo" is created
Further, the relationship between the above objects are:
The Execution Context of a call to function "foo":
.Activation object: = Variable object
.arguments
.local variables
.local function declarations
.Scope chain:
= Activation object + foo.scope
The global execution context gets some slightly different handling:
1. As it does not have arguments so it does not need a defined Activation object to refer to them;
2. The global execution context does need a scope and its scope chain consists of exactly one object, the global object itself (window object).
3. The global execution context does go through variable instantiation just like any other context;
4. The global object itself is used as the Variable object, which is why global functions and variables become properties of the global object.
How Does A JavaScript Function Get Executed?
The JavaScript engine goes through several steps in calling a JavaScript function (say the function is named "foo" for reference purposes):
length and callee properties specified. This argument object is an array-like object with integer indexed members corresponding to the arguments passed to the function call, in the order of their appearance. It also has length and callee properties instantiated. Next, a property named "arguments" is created for the Activation Object and a reference to this arguments object is assigned to this property; Activation Object, with value being “undefined” for local variables and non-instantiated function call parameters (more on the values later, and also more on how functions are instantiated and assigned [[scope]] property later). "this" keyword later); Now the execution context of this function call is created and we are ready to call the function.
window["variableName"] would be better than using "variableName" directly);More importantly to point out, the [[scope]] property of this newly created function object "foo3" is the scope chain of the execution context where the instantiation happens - which is “context foo2″ in this case. So "foo3.scope" is (”Activation object foo2″+ the scope chain of “foo”);
"foo"; "foo" completes and we return to the callee. At this moment, unless a closure is formed, this execution context is available for garbage collection.Function Instantiation
In JavaScript, “functions” are objects. Depending on how a function is defined, there are three ways to instantiate a function object.
[[scope]] property assigned to be the scope chain of the execution context in which this function object is created. function outerFunc() {
var var1=10;
function MyFunc1(){
return var1=var1+1;
};
return MyFunc1();
}
alert(outerFunc()); //displays "11".
When the host environment is going to execute line alert(outerFunc());, in particular, when it is going to call outerFunc(), it will create a new Execution Context for this call (let’s name this new execution context outerFuncContext1 for reference purpose). During the creation of outerFuncContext1, it will instantiate variables. Both "var1" and "MyFunc1" are instantiated during this variable instantiation. Upon variable instantiation, the Activation Object associated with outerFuncContext1 will have a property named "var1" whose value is “undefined”, and a property named "MyFunc1" whose value is a function object created here. Further, the [[scope]] property of this function object is assigned to be the scope chain of outerFuncContext1.
function outerFunc() {
var var1=10;
var var2=function(){
return var1=var1+1;
};
return var2();
}
alert(outerFunc()); //displays "11".
When the host environment is going to call outerFunc(), it will create a new Execution Context for this call (again, let’s name this execution context outerFuncContext1 for reference purpose). During the creation of outerFuncContext1, it will instantiate variables. Both "var1" and "var2" are created during this variable instantiation. Upon variable instantiation, the Activation Object associated with outerFuncContext1 will have a property named "var1" whose value is “undefined”, and a property named "var2" whose value is also “undefined”.
Then the host environment goes on to execute the call to outerFunc(). During execution, first it will assign 10 to var1. Then it will evaluate the function expression related to var2. During this evaluation, a function object is created and assigned to variable var2. Further, the [[scope]] property of this function object is assigned to be the scope chain of the execution context in which this function object is created, outerFuncContext1.
Function constructor:[[scope]] property always refers to the scope chain of the global execution context, which contains only one object, the global object. An example is:
var y=100;
var multiply = new Function("x", "return x * y;");
alert(10); //displays 1000.
In the above example, variable y is being searched in the global context. If there is no global variable named "y", an error will be thrown.
[[scope]] property that refers to a scope chain. This property is very important for figuring out the scope of execution context and thus variable resolution, etc:Function constructor, the [[scope]] property always refers to one object only, the global object. On browsers, it is the "window" object. [[scope]] property always refers to the scope chain of the execution context in which the function object is created. The "this" Keyword
One of the most powerful JavaScript keywords is "this" and I am sure that every developer has used it many times. However, it is not straightforward to figure out what it points to.
"this" is typically assigned an object that it points to during the process of creating a new execution context. If the value assigned refers to an object, then property accessors prefixed with the "this" keyword points to the properties of that object. However, if no object can be assigned to it, then the "this" keyword will point to the global object by default.
The next question is how do we know when/whether an Object will be assigned to "this" and who that Object is. For example, consider the following code:
function redColor() {
this.color= 'red';
}
alert(redColor.color); //shows "undefined"
var aa=redColor;
alert("color="+aa.color); //shows color= undefined
What does not “color” become a property of "aa"? Further, functions are objects in JavaScript - why wouldn’t "this" refer to the function object “redColor” itself?
The rule to remember is that "this" always refers to the “owner” of the function we’re executing. In other words, keyword "this" always refers to the object that the containing function is a method of.
In the above example, the function "redColor" is not a method of object "redColor", so "redColor.color" returns “undefined”. Further, function "redColor" is not a method of some object called "aa" either. So "aa.color" also returns “undefined”.
However, according to our understanding of how variable instantiation works, "redColor" is instantiated as a property of the global object. The global object has a property called "redColor" and the value of the property is the "redColor" function object. In the end, "function redColor" is actually a method of the global object. According to the rule above, "this" should be referring to the global object.
In other words, if we execute function "redColor", we are actually assigning a “color” property to the global object with the value of “red”. If you run the following code:
function redColor() {
this.color = 'red';
}
redColor();
alert("window.color="+window.color);//shows window.color=red
The alert box shows “window.color=red”!
If you attach function "redColor" as a method of an object using obj.method=redColor or obj.prototype.methodName=redColor, the keyword "this" will correctly point to our "obj". However, there is one case that people tend to make mistake, like the following:
<div id="mydiv" onclick="redColor();">This is my div</div>
"redColor" is still not a method of the “div” object. It is being called by a method of the “div” object called the "onclick". As a result, the "this" keywork in the function body of "redColor" is not pointing to the “div” object as we hoped. The code below is the right way to achieve the goal of adding a "color" property to the “div” Object:
var div=document.getElementById("mydiv");
div.onclick=redColor;
The "eval" and "setTimeout" Functions
Function "eval" and "setTimeout" have their own distinct execution context, as mentioned briefly ealier. "setTimeout" always assumes the global execution context. For "eval", here is the quote from ECMASpecification:
“When control enters an execution context for eval code, the previous active execution context, referred to as the calling context, is used to determine the scope chain, the variable object, and the this value. If there is no calling context, then initializing the scope chain, variable instantiation, and determination of the this value are performed just as for global code.
The scope chain is initialized to contain the same objects, in the same order, as the calling context’s
scope chain. This includes objects added to the calling context’s scope chain by with statements and
catch clauses.
Variable instantiation is performed using the calling context’s variable object and using empty
property attributes.
The this value is the same as the this value of the calling context.”
In short, you can think of “eval” assuming the exact same execution context as the calling code.