Deep understandingJavaScriptSeries ( -): Scope chain(Scope Chain)Objective
In the 12th chapter of the description of variable objects, we already know that the data of an execution context (variables, function declarations, and formal parameters of functions) is stored as attributes in the variable object.
We also know that the variable object is created each time the context is entered, and the initial value is filled in, and the update of the value appears in the Code execution phase.
This chapter deals with more details that are directly related to the execution context, and this time we will mention a topic-the scope chain.
Defined
If you want to briefly describe and show the focus, the scope chain is mostly related to intrinsic functions.
We know that ECMAScript allows you to create intrinsic functions, and we can even return these functions from the parent function.
var x = 10;
function foo () {
var y = 20;
function Bar () {
Alert (x + y);
}
return bar;
}
Foo () (); 30
Thus, it is clear that each context has its own variable object: For the global context, it is the global object itself, and for a function, it is the active object.
The scope chain is the list of all variable objects (including parent variable objects) in the internal context. This chain is used for variable queries. In the example above, the scope chain of the "bar" context includes AO (bar), AO (foo), and VO (global).
But let's look at the problem carefully.
Let's start with the definition and discuss the example in depth.
A scope chain is associated with an execution context, and a chain of variable objects is used for variable lookups in identifier parsing.
The scope chain of a function context is created when the function is called, containing the active object and the [[scope]] property inside the function. Below we will discuss a function's [[scope]] property in more detail.
The following is illustrated in the context:
Activeexecutioncontext = {
VO: {...},//or AO
This:thisvalue,
Scope: [//Scope chain
List of all variable objects
For identifiers Lookup
]
};
Its scope is defined as follows:
Scope = AO + [[Scope]]
This association and identifier parsing process, which we will discuss below, is related to the life cycle of the function.
The life cycle of a function
The life cycle of a function is divided into the creation and activation phases (when called), so let's examine it in detail.
function creation
It is well known that the function declaration is placed in the variable/activity (Vo/ao) object when the context is entered. Let's look at the variables and function declarations in the global context (where the variable object is the global object itself, we remember, right?) )
var x = 10;
function foo () {
var y = 20;
Alert (x + y);
}
Foo (); 30
When the function is activated, we get the correct (expected) result--30. However, there is a very important feature.
Previously, we only talked about variable objects in the current context. Here, we see that the variable "y" is defined in the function "foo" (meaning it is in the AO of the Foo context), but the variable "x" is not defined in the "foo" context and, accordingly, it is not added to the ao of "foo". At first glance, the variable "x" does not exist in relation to the function "foo" at all, but as we see below-and just "at a glance", we find that the active object in the "foo" context contains only one attribute-"Y".
Foocontext.ao = {
y:undefined//Undefined – When entering the context is the - at activation
};
How does the function "foo" access the variable "x"? Theoretically, a function should be able to access a variable object of a higher level of context. It is, in fact, a mechanism that is implemented through the [[scope]] property within the function.
[[Scope]] is a hierarchical chain of all parent variable objects, above the current function context, and stored in the function when it is created.
Note that this important point--[[scope]] is stored when the function is created-static (unchanging), forever, until the function is destroyed. That is, the function can never be called, but the [scope] property has been written and stored in the function object.
Another thing to consider--in contrast to the scope chain, [[scope]] is a property of a function rather than a context. Considering the example above, the function "foo" [[scope]] is as follows:
Foo. [[Scope]] = [
GLOBALCONTEXT.VO//= = = Global
];
For example, we use the usual ECMAScript array to show scopes and [[scope]].
Continue, we know that when a function call enters the context, the active object is created, this and scope (scope chain) is determined. Let us consider this moment in detail.
function activation
As mentioned in the definition, the scope property of the context (a scope chain for a variable lookup) is defined as follows when the context is created AO/VO:
Scope = ao| VO + [[Scope]]
The above code means that the active object is the first object of the scope array, that is, the front end of the scope to be added.
Scope = [Ao].concat ([[Scope]]);
This feature is important for the processing of identifier parsing.
Identifier parsing is a process used to determine which variable object a variable (or function declaration) belongs to.
In the return value of this algorithm, we always have a reference type whose base component is the corresponding variable object (or null if not found), and the property name component is the name of the identifier that looks up. The details of the reference type are discussed in Chapter 13th.
The identifier parsing process contains a lookup for the property that corresponds to the variable name, that is, a continuous lookup of variable objects in the scope, starting with the deepest context, bypassing the scope chain until the topmost level.
In this way, a local variable in a context has a higher precedence than a variable in the parent scope in an upward lookup. In case two variables have the same name but come from a different scope, the first one is found in the deepest scope.
We use a slightly more complex example to describe the above mentioned.
var x = 10;
function foo () {
var y = 20;
function Bar () {
var z = 30;
Alert (x + y + z);
}
Bar ();
}
Foo (); 60
For this, we have the following variable/activity object, the [[scope]] property of the function and the scope chain of the context:
The variable object for the global context is:
Globalcontext.vo = = = Global = {
X:10
Foo: <reference to Function>
};
When "foo" is created, the [scope] property of "foo" is:
Foo. [[Scope]] = [
Globalcontext.vo
];
When "foo" is activated (into context), the active object of the "foo" context is:
Foocontext.ao = {
Y:20,
Bar: <reference to Function>
};
The scope chain for the "foo" context is:
Foocontext.scope = Foocontext.ao + foo. [[Scope]]//i.e.:
Foocontext.scope = [
Foocontext.ao,
Globalcontext.vo
];
When the inner function "bar" is created, its [[scope]] is:
Bar. [[Scope]] = [
Foocontext.ao,
Globalcontext.vo
];
When "Bar" is activated, the active object in the "bar" context is:
Barcontext.ao = {
Z:30
};
The scope chain for the "bar" context is:
Barcontext.scope = Barcontext.ao + Bar. [[Scope]]//i.e.:
Barcontext.scope = [
Barcontext.ao,
Foocontext.ao,
Globalcontext.vo
];
The identifiers for "X", "Y", "Z" are resolved as follows:
-"X"
--Barcontext.ao//not found
--Foocontext.ao//not found
--GLOBALCONTEXT.VO//FOUND-10
-"Y"
--Barcontext.ao//not found
--Foocontext.ao//found-20
-"Z"
--Barcontext.ao//found-30
Scope characteristics
Let's look at some important characteristics related to the scope chain and function [[scope]] properties.
Closed Package
In ECMAScript, closures are directly related to [[scope]] of the function, as we mentioned, [[scope] is stored when the function is created, and the function is a common survival. In fact, a closure is a combination of the function code and its [[scope]]. Therefore, as one of its objects, [[Scope]] includes the lexical scope (parent variable object) created within the function. When a function is further activated, a variable from a higher scope is searched in the lexical chain of the variable object (when it is statically stored at the time of creation).
For example:
var x = 10;
function foo () {
alert (x);
}
(function () {
var x = 20;
Foo (); Ten, but not 20
})();
We see again that during the identifier parsing process, the lexical scope defined when the function was created-the variable resolves to 10 instead of 30. Also, this example clearly shows that a function (in this case, the anonymous function returned from the function "foo") persists, even after the scope of the function creation has been completed.
For more details on the theory of closure in ECMAScript and its execution mechanism, read Chapter 16 closures.
Functions created by constructors [[scope]]
In the example above, we see that when the function is created, we get the [scope] property of the function, which accesses the variables of all the parent contexts. However, there is an important exception to this rule, which involves functions created through function constructors.
var x = 10;
Function foo () {
var y = 20;
function barfd () { // function declaration
alert (x);
alert (y);
}
var barFE = function () { // function expression
alert (x);
alert (y);
};
var barfn = function (' Alert (x); alert (y); ');
barfd (); // 10, 20
barfe (); // 10, 20
barfn (); // 10, "y" is not defined
}
Foo ();
As we can see, the function "bar" created by the function constructor function constructor cannot access the variable "y". This does not mean that the function "BARFN" does not have a [[scope]] property (otherwise it cannot access the variable "x"). The problem is that the [scope] property of the function created by the letter constructor is always a unique global object. With this in mind, it is not possible, as with this function, to create the topmost context closure in addition to the global.
Two-dimensional scope chain lookup
The most important thing to look for in the scope chain is that the properties of the variable object, if any, should be taken into account-the prototype characteristics that originate from ECMAScript. If an attribute is not directly found in the object, the query continues in the prototype chain. That is often said to find the two-dimensional chain. (1) Scope chain link, (2) Each scope chain-deep into the prototype chain link. If you define a property in Object.prototype, we can see this effect.
function foo () {
alert (x);
}
object.prototype.x = 10;
Foo (); 10
The active object does not have a prototype, which we can see in the following example:
function foo () {
var x = 20;
function Bar () {
alert (x);
}
Bar ();
}
object.prototype.x = 10;
Foo (); 20
If the active object of the function "bar" context has a prototype, then "X" is parsed in Object.prototype because it is not parsed directly in the AO. But in the first example above, in identifier parsing, we get to the global object (not all in some executions), it inherits from Object.prototype, and in response, "X" resolves to 10.
The same situation occurs in some versions of Spidermokey's named function expressions (abbreviated as NFE), Where a particular object stores the optional name of a function expression inherited from Object.prototype, in some versions of BlackBerry, the activation object is inherited from Object.prototype when executed. However, more details about this feature are discussed in the 15th chapter of the function.
Scope chains in the global and eval contexts
It's not necessarily interesting here, but it has to be prompted. The scope chain of the global context contains only global objects. The context of code eval has the same scope chain as the current invocation context (calling context).
Globalcontext.scope = [
Global
];
Evalcontext.scope = = = Callingcontext.scope;
Impact of code execution on scope chains
In ECMAScript, there are two declarations in the code execution phase that can modify a scope chain. This is the with declaration and catch statement. They are added to the forefront of the scope chain, and objects must be looked up in the identifiers that appear in these declarations. If one occurs, the scope chain is briefly modified as follows:
Scope = Withobject|catchobject + ao| VO + [[Scope]]
To add an object to this example, the object is its argument (so that, without a prefix, the object's properties become accessible).
var foo = {x:10, y:20};
With (foo) {
alert (x); 10
alert (y); 20
}
The scope chain changes to this:
Scope = foo + ao| VO + [[Scope]]
Again we see that with the WITH statement, the parsing of identifiers in objects is added to the forefront of the scope chain:
var x = ten, y = 10;
With ({x:20}) {
var x = +, y = 30;
alert (x); 30
alert (y); 30
}
alert (x); 10
alert (y); 30
What happens when you enter the context? The identifiers "X" and "Y" have been added to the variable object. In addition, the following modifications are made during the code run phase:
- x = ten, y = 10;
- Object {x:20} is added to the front end of the scope;
- Within with, the Var declaration was encountered and of course nothing was created because all variables were added when the context was entered;
- In the second step, only the variable "x" is modified, in fact the "X" in the object is now parsed and added to the front of the scope chain, "X" is 20 and becomes 30;
- Also has the variable object "Y" the modification, after being parsed its value also corresponding changes from 10 to 30;
- In addition, after the with declaration is complete, its specific object is removed from the scope chain (the changed variable "x"--30 is also removed from that object), that is, the structure of the scope chain reverts to the previous state with which it was strengthened.
- In the last two alert, the "X" of the current variable object remains the same, and the value of "Y" is now equal to 30, which has changed in the with declaration run.
Similarly, the exception parameter of the catch statement becomes accessible, creating a new object with only one property-the name of the exception parameter. The diagram looks like this:
try {
...
} catch (ex) {
Alert (ex);
}
The scope chain is modified to:
var catchobject = {
Ex: <exception object>
};
Scope = Catchobject + ao| VO + [[Scope]]
After the catch statement finishes running, the scope chain reverts to its previous state.
Conclusion
At this stage, we almost consider all the common concepts associated with the execution context, and the details associated with them. Follow the detailed analysis of the plan-function object: function type (function declaration, function expression) and closure. By the way, in this article, closures are directly related to the [[scope]] property, however, about it will be discussed in the appropriate chapter. I'd be happy to answer your question in the comments.
Ext.: http://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html
JavaScript scope chain