Objective
In the 12th chapter, in the description of variable objects, we already know that the data (variables, function declarations, and function parameters) of an execution context are 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 is devoted to more details directly related to the execution context, and this time we will mention an issue-scope chain.
Original English: http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/
Chinese reference: http://www.denisdeng.com/?p=908
The majority of this article from the above address, only a little change, thank the author
Defined
If you want to briefly describe and highlight the focus, most of the scope chain is related to the intrinsic function.
We know that ECMAScript allows the creation of intrinsic functions, and we can even return them from the parent function.
Copy Code code as follows:
var x = 10;
function foo () {
var y = 20;
function Bar () {
Alert (x + y);
}
return bar;
}
Foo () (); 30
In this way, it is obvious that each context has its own variable object: For the global context, it is the global object itself, and for the function, it is the active object.
The scope chain is the list of all variable objects (including the parent variable objects) within 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 a definition and go deep into the discussion example.
The scope chain is related to an execution context, and a chain of variable objects is used to find variables in identifier resolution.
The scope chain of the function context is created when the function is called, containing the active object and the [[scope] property inside the function. We'll discuss the [[scope]] property of a function in more detail below.
The following is indicated in the context:
Copy Code code as follows:
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 Union and identifier parsing process, which we will discuss below, is related to the lifecycle of the function.
The life cycle of a function
The lifecycle of a function is divided into the creation and activation phases (call time), so let's look at it in detail.
function creation
It is well known that a function declaration is placed in a variable/activity (Vo/ao) object when it enters the context. Let's take a look at the variables and function declarations in the global context where the variable object is the global object itself, we remember, right? )
Copy Code code as follows:
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 talked only 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, correspondingly, it is not added to the ao of "foo". At first glance, the variable "x" does not exist in relation to the function "foo", but as we see below-and just a glimpse, we find that the active object of the "foo" context contains only one attribute--"Y".
Copy Code code as follows:
Foocontext.ao = {
y:undefined//undefined– Enter the context is 20–at activation
};
How does the function "foo" access the variable "x"? Theoretically the function should be able to access a variable object in a higher level context. It is actually the case that this mechanism is implemented through the [[scope]] attribute within the function.
[[Scope]] is the hierarchy chain of all parent variable objects, above the current function context, which is stored when the function is created.
Note that this important point--[[scope] is stored when the function is created-static (immutable), forever and ever, until the function is destroyed. That is, a function can never be invoked, but the [[scope]] property has been written and stored in the function object.
Another thing to consider is that, in contrast to the scope chain, [[scope]] is a property of a function, not a context. Given the example above, the [[scope]] of the function "Foo" is as follows:
Copy Code code as follows:
Foo. [[Scope]] = [
GLOBALCONTEXT.VO//= = Global
];
For example, we use the usual ECMAScript array to show the scope and [[scope]].
Continue, we know that when the function is called the context is entered, when the active object is created, this and scope (scope chain) is determined. Let's consider this moment in detail.
function activation
As mentioned in the definition, the scope property of the context (a scope chain of the variable lookup) is defined as follows when the context is created AO/VO:
Scope = ao| VO + [[Scope]]
The code above means that the active object is the first object in the scope array, which is added to the front of the scope.
Scope = [Ao].concat ([[Scope]]);
This feature is important for the processing of identifier parsing.
Identifier resolution is a process that determines 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 it is not found), and the property name component is the name of the identifier to look up. The details of the reference type are discussed in Chapter 13th. this.
The identifier parsing process contains a lookup of the corresponding attribute of the variable name, the continuous lookup of the variable object in the scope, starting from the deepest context, bypassing the scope chain until the topmost level.
As a result, in an upward lookup, a local variable in one context has a higher priority than a variable in the parent scope. In case two variables have the same name but come from different scopes, the first one is found in the deepest scope.
We use a slightly more complex example to describe the above.
Copy Code code as follows:
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/active object, the [[scope]] property of the function, and the scope chain of the context:
The variable objects for the global context are:
Copy Code code as follows:
Globalcontext.vo = = Global = {
X:10
Foo: <reference to Function>
};
When "foo" is created, the [[scope]] property of "foo" is:
Copy Code code as follows:
Foo. [[Scope]] = [
Globalcontext.vo
];
When "foo" is activated (enter context), the active object for the "foo" context is:
Copy Code code as follows:
Foocontext.ao = {
Y:20,
Bar: <reference to Function>
};
The scope chain for the "foo" context is:
Copy Code code as follows:
Foocontext.scope = Foocontext.ao + foo. [[Scope]]//i.e.:
Foocontext.scope = [
Foocontext.ao,
Globalcontext.vo
];
When the internal function "bar" is created, its [[scope]] is:
Copy Code code as follows:
Bar. [[Scope]] = [
Foocontext.ao,
Globalcontext.vo
];
When bar is activated, the active object of the bar context is:
Copy Code code as follows:
Barcontext.ao = {
Z:30
};
The scope chain of the "bar" context is:
Copy Code code as follows:
Barcontext.scope = Barcontext.ao + Bar. [[Scope]]//i.e.:
Barcontext.scope = [
Barcontext.ao,
Foocontext.ao,
Globalcontext.vo
];
The identifiers for "X", "Y", and "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 features related to the scope chain and the function [[scope]] property.
Closed Bag
In ECMAScript, closures are directly related to the [[scope]] of a function, as we mentioned, [[scope]] is stored when the function is created, and survives the function. In fact, closures are a combination of functional code and its [[scope]]. Therefore, as one of its objects, [[Scope]] includes the lexical scope (the parent variable object) that is created within the function. When the function is further activated, the variable from the higher scope is searched in this lexical chain of the variable object (which is stored statically when created).
For example:
Copy Code code as follows:
var x = 10;
function foo () {
alert (x);
}
(function () {
var x = 20;
Foo (); But not 20
})();
We see again, in the identifier parsing process, the lexical scope defined using function creation--the variable resolves to 10 instead of 30. In addition, this example also shows clearly that a function (in this case, the anonymous function returned from the function "foo") persists, even after the function creation scope has been completed.
For more details on the theory of closure in ECMAScript and its implementation mechanism, read Chapter 16 closures.
A function created by a constructor [[scope]]
In the above example, we saw that the [[scope]] property of the function was obtained at the time the function was created, through which the variables of all parent contexts were accessed. However, there is an important exception to this rule, which involves functions created by function constructors.
Copy Code code as follows:
var x = 10;
function foo () {
var y = 20;
function Barfd () {//Functions Declaration
alert (x);
alert (y);
}
var Barfe = function () {//functions expression
alert (x);
alert (y);
};
var barfn = Function (' alert (x); alert (y); ');
BARFD (); 10, 20
Barfe (); 10, 20
BARFN (); "Y" is not defined
}
Foo ();
We see that the function "bar" created by the function constructor (functions 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 functions created by the function constructor is always the only global object. With this in mind, it is not possible to create a top-level contextual closure in addition to the global by using this function.
Two-dimensional scope chain lookup
One of the most important things to look for in the scope chain is the attribute of the variable object (if any) to take into consideration--the prototype feature from ECMAScript. If an attribute is not found directly in the object, the query continues in the prototype chain. It is often said that two-dimensional chain lookup. (1) Scope chain link, (2) Each scope chain-depth to the prototype chain link. If you define a property in Object.prototype, we can see this effect.
Copy Code code as follows:
function foo () {
alert (x);
}
object.prototype.x = 10;
Foo (); 10
The active object has no prototype, and we can see in the following example:
Copy Code code as follows:
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 directly parsed in AO. But in the first example, in identifier parsing, we get to the global object (not all of it in some executions), it inherits from Object.prototype, and in response, "X" resolves to 10.
The same situation appears in some versions of Spidermokey named function expressions (abbreviated NFE), Where a particular object stores an optional name for a function expression inherited from Object.prototype, in some versions of the BlackBerry, the activation object inherits from Object.prototype when executed. However, more details about the feature are discussed in the 15th chapter function.
Scope chain in global and eval contexts
This is not necessarily very interesting, but you have to be prompt. The scope chain of the global context contains only global objects. The context of the code eval has the same scope chain as the current invocation context (calling).
Copy Code code as follows:
Globalcontext.scope = [
Global
];
Evalcontext.scope = = Callingcontext.scope;
The effect of code execution on the scope chain
In ECMAScript, there are two declarations in the code execution phase that can modify the scope chain. This is the with declaration and catch statement. They are added to the front end of the scope chain, and objects must be found in identifiers that appear in those declarations. If one of these occurs, the scope chain is briefly modified as follows:
Scope = Withobject|catchobject + ao| VO + [[Scope]]
To add an object in this example, the object is its argument (so that the object's properties become accessible without a prefix).
Copy Code code as follows:
var foo = {x:10, y:20};
With (foo) {
alert (x); 10
alert (y); 20
}
The scope chain is modified to this way:
Scope = foo + ao| VO + [[Scope]]
We see again, through the WITH statement, that the resolution of identifiers in the object is added to the front end of the scope chain:
Copy Code code as follows:
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 identifier "x" and "Y" have been added to the variable object. In addition, make the following modifications in the code run phase:
x = ten, y = 10;
The object {x:20} is added to the front of the scope;
Within the with, the Var declaration was encountered, and of course nothing was created because all the variables were parsed and added when the context was entered;
In the second step, only the variable "x" is modified, and in fact the "X" in the object is now parsed and added to the front of the scope chain, "X" being 20, and becoming 30;
Also has the variable object "Y" the modification, after parsing its value also to change 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 is restored to the previous state where the with is 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, and changes have occurred in the with declaration run.
Similarly, the exception parameter of a catch statement becomes accessible, creating a new object with only one attribute-the name of the exception parameter. The diagram looks like this:
Copy Code code as follows:
try {
...
catch (ex) {
Alert (ex);
}
The scope chain is modified to:
Copy Code code as follows:
var catchobject = {
Ex: <exception object>
};
Scope = Catchobject + ao| VO + [[Scope]]
After the catch statement completes running, the scope chain reverts to its previous state.
Conclusion
At this stage, we consider almost all the common concepts associated with the execution context, as well as the details associated with them. Follow the plan--detailed analysis of Function objects: function types (function declarations, function expressions), and closures. By the way, in this article, the closure is directly related to the [[scope]] attribute, but it will be discussed in the appropriate chapter. I'd be happy to answer your questions in the comments.
Other Reference
- 8.6.2–[[scope]]
- 10.1.4–scope Chain and Identifier resolution