您的位置:首页 > 大数据 > 人工智能

Ecmascript-262-3 in detail. Chapter 4. Scope chain.

2015-07-11 01:02 525 查看
Read this article in: Russian,
Chinese (version 1, version
2), Korean.

Introduction
Definition
Function life cycle

Function creation
Function activation

Scope features

Closures
[[Scope]]
of functions created via Function constructor
Two-dimensional Scope chain
lookup
Scope
chain of the global and eval contexts
Affecting
on Scope chain during code execution

Conclusion
Additional literature


Introduction

As we already know from the second chapter concerning the variable object, the data of an execution
context (variables, function declarations, and formal parameters of functions) are stored as properties of the variables object.

Also, we know that the variable object is created and filled with initial values every time on entering
the context, and that its updating occurs at code execution phase.

This chapter is devoted one more detail directly related with execution contexts; this time, we will mention a topic of a scope chain.


Definition

If to describe briefly and showing the main point, a scope chain is mostly related with inner functions.

As we know, ECMAScript allows creation of inner functions and we can even return these functions from parent functions.

Thus, is known that every context has its own variables object: for the global context it is global objectitself, for functions it is the activation object.

And the scope chain is exactly this list of all (parent) variable objects for the inner contexts. This chain is used for variables lookup. I.e. in the example above, scope chain of “bar” context includes AO(bar), AO(foo) and VO(global).

But, let’s examine this topic in detail.

Let’s begin with the definition and further will discuss deeper on examples.

Scope chain is related with an execution context a chain of variable objects which is used for variables lookup at identifier resolution.

The scope chain of a function context is created at function call and consists of the activation objectand the internal [[Scope]] property of this function. We will discuss the [[Scope]] property of a function in detail below.

Schematically in the context:

where Scope by definition is:

For our examples we can represent Scope, and [[Scope]] as normal ECMAScript arrays:

The alternative structure view can be represented as a hierarchical object chain with the reference to the parent scope (to the parent variable object) on every link of the chain. For this view corresponds __parent__ concept of some implementations
which we discussed in the second chapter devoted
variable object:

But to represent a scope chain using an array is more convenient, so we will use this approach. Besides, the specification statements abstractly itself (see 10.1.4)
that “a scope chain is a list of objects”, regardless that on the implementation level can be used the approach with the hierarchical chain involving the __parent__ feature. And the array abstract representation is a good candidate for the list concept.

The combination AO + [[Scope]] and also process of identifier resolution, which we will discuss below, are related with the life cycle of functions.


Function life cycle

Function life cycle is divided into a stage of creation and a stage of activation (call). Let’s consider them in detail.


Function creation

As is known, function declarations are put into variable/activation object (VO/AO) on entering the context stage. Let’s see on the example a variable and a function declaration in the global context (where variable object is the global object itself, we remember,
yes?):

At function activation, we see correct (and expected) result – 30. However, there is one very important feature.

Before this moment we spoke only about variable object of the current context. Here we see that “y” variable is defined in function “foo” (which means it is in the AO of “foo” context), but variable “x” is not defined in context of “foo” and accordingly is
not added into the AO of “foo”. At first glance “x” variable does not exist at all for function “foo”; but as we will see below — only “at first glance”. We see that the activation object of “foo” context contains only one property — property “y”:

How does function “foo” have access to “x” variable? It is logical to assume that function should have access to the variable object of a higher context. In effect, it is exactly so and, physically this mechanism is implemented via the internal [[Scope]] property
of a function.

[[Scope]] is a hierarchical chain of all parent variable objects, which are above the current function context; the chain is saved to the function at its creation.

Notice the important point — [[Scope]] is saved at function creation — statically (invariably), once and forever — until function destruction. I.e. function can be never called, but [[Scope]] property is already written and stored in
function object.

Another moment which should be considered is that [[Scope]] in contrast with Scope (Scope chain) is the property of a function instead of a context. Considering the above example, [[Scope]] of the “foo” function is the following:

And further, by a function call as we know, there is an entering a function context where the activation object is created and this value and Scope (Scope chain) are determined. Let us consider this moment in detail.


Function activation

As it has been said in definition, on entering the context and after creation of AO/VO, Scope property of the context (which is a scope chain for variables lookup) is defined as follows:

High light here is that the activation object is the first element of the Scope array, i.e. added to the front of scope chain:

This feature is very important for the process of identifier resolution.

Identifier resolution is a process of determination to which variable object in scope chain the variable (or the function declaration) belongs.

On return from this algorithm we have always a value of type Reference, which base component is the corresponding variable object (or null if variable is not found), and a property name component is the name of the looked
up (resolved) identifier. In detail Reference type is discussed in the Chapter 3. This.

Process of identifier resolution includes lookup of the property corresponding to the name of the variable, i.e. there is a consecutive examination of variable objects in the scope chain, starting from the deepest context and up to the top of the scope chain.

Thus, local variables of a context at lookup have higher priority than variables from parent contexts, and in case of two variables with the same name but from different contexts, the first is found the variable of deeper context.

Let’s a little complicate an example described above and add additional inner level:

For which we have the following variable/activation objects, [[Scope]] properties of functions and scope chains of contexts:

Variable object of the global context is:

At
foo
creation, the
[[Scope]]
property
of
foo
is:

At
foo
function call, the activation object of
foo
context
is:

And the scope chain of
foo
context is:

At creation of inner
bar
function its
[[Scope]]
is:

At
bar
function call, the activation object of
bar
context
is:

And the scope chain of
bar
context is:

Identifier resolution for
x
,
y
and
z
names:


Scope features

Let’s consider some important features related with Scope chain and
[[Scope]]
property of functions.


Closures

Closures in ECMAScript are directly related with the
[[Scope]]
property of functions. As it has been noted,
[[Scope]]
is
saved at function creation and exists until the function object is destroyed. Actually, a closure is exactly a combination of a function code and its
[[Scope]]
property.
Thus,
[[Scope]]
contains that lexical environment (the parent variable object) in which function is created.
Variables from higher contexts at the further function activation will be searched in this lexical (statically saved at creation) chain of variable objects.

Examples:

We see that
x
variable is found in the
[[Scope]]
of
foo
function,
i.e. for variables lookup the lexical (closured) chain defined at the moment of function creation, but not the dynamic chain of the call (at which value of
x
variable
would be resolved to
20
) is used.

Another (classical) example of closure:

Again we see that for the identifier resolution the lexical scope chain defined at function creation is used — the variable
x
is
resolved to
10
, but not to
30
.
Moreover, this example clearly shows that
[[Scope]]
of a function (in this case of the anonymous function returned from function
foo
)
continues to exist even after the context in which a function is created is already finished.

In more details about the theory of closures and their implementation in ECMAScript read in the Chapter 6. Closures.


[[Scope]] of functions created via Function constructor

In the examples above we see that function at creation gets the
[[Scope]]
property and via this property it accesses variables of
all parent contexts. However, in this rule there is one important exception, and it concerns functions created via the Function constructor.

As we see, for
barFn
function which is created via the
Function
constructor
the variable
y
is not accessible. But it does not mean that function
barFn
has
no internal
[[Scope]]
property (else it would not have access to the variable
x
).
And the matter is that
[[Scope]]
property of functions created via the
Function
constructor
contains always only the global object. Consider it since, for example, to create closure of upper contexts, except global, via such function is not possible.


Two-dimensional Scope chain lookup

Also, an important point at lookup in scope chain is that prototypes (if they are) of variable objects can be also considered — because of prototypical nature of ECMAScript: if property is not found directly in the object, its lookup proceeds in the prototype
chain. I.e. some kind of 2D-lookup of the chain: (1) on scope chain links, (2) and on every of scope chain link — deep into on prototype chain links. We can observe this effect if define property in
Object.prototype
:

Activation objects do not have prototypes what we can see in the following example:

If activation object of
bar
function context would have a prototype, then property
x
should
be resolved in
Object.prototype
because it is not resolved directly in AO. But in the first example above, traversing the scope
chain in identifier resolution, we reach the global object which (in some implementation but not in all) is inherited from
Object.prototype
and,
accordingly,
x
is resolved to
10
.

The similar situation can be observed in some versions of SpiderMokey with named function expressions (abbreviated form is NFE), where special object which stores the optional name of function-expression is inherited from
Object.prototype
,
and also in some versions of Blackberryimplementation where activation objects are inherited from Object.prototype. But more detailed this features are discussed in Chapter
5. Functions.


Scope chain of the global and eval contexts

Here is not so much interesting, but it is necessary to note. The scope chain of the global context contains only global object. The context with code type “eval” has the same scope chain as a calling context.


Affecting on Scope chain during code execution

In ECMAScript there are two statements which can modify scope chain at runtime code execution phase. These are with statement and catch clause. Both of them add to the front of scope chain the object required for lookup identifiers appearing
within these statements. I.e., if one of these case takes place, scope chain is schematically modified as follows:

The statement with in this case adds the object which is its parameter (and thus properties of this object become accessible without prefix):

Scope chain modification:

Let us show once again that the identifier is resolved in the object added by the with statement to the front of scope chain:

What happened here? On entering the context phase, “x” and “y” identifiers have been added into the variable object. Further, already at runtime code executions stage, following modifications have been made:

x = 10, y = 10;
the object {x: 20} is added to the front of scope chain;
the met var statement inside with, of course, created nothing, because all variables have been parsed and added on entering the context stage;
there is only modification of “x” value, and exactly that “x” which is resolved now in the object added to the front of scope chain at second step; value of this “x” was 20, and became 30;
also there is modification of “y” which is resolved in variable object above; accordingly, was 10, became 30;
further, after with statement is finished, its special objects is removed from the scope chain (and the changed value “x” – 30 is removed also with that object), i.e. scope chain structure is restored to the previous state
which was before with statement augmentation;
as we see in last two alerts: the value of “x” in current variable object remains the same and the value of “y” is equal now to 30 and has been changed at with statement work.

Also, a
catch
clause in order to have access to the parameter-exception creates an intermediate scope object with the only property
— exception parameter name, and places this object in front of the scope chain. Schematically it looks so:

Scope chain modification:

After the work of catch clause is finished, scope chain is also restored to the previous state.


Conclusion

At this stage, we have considerate almost all general concepts concerning execution contexts and related with them details. Further, according to plan, — detailed analysis of function objects: types of functions (FunctionDeclaration, FunctionExpression) and closures.
By the way, closures are directly related with the [[Scope]] property discussed in this article, but about it is in appropriate chapter. I will be glad to answer your questions in comments.


Additional literature

8.6.2 – [[Scope]]
10.1.4 – Scope Chain and Identifier Resolution

Translated by: Dmitry A. Soshnikov.

Published on: 2010-03-21

Originally written by: Dmitry A. Soshnikov [ru, read »]

Originally published on: 2009-07-01
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: