In-depth understanding of ScopeChain in JavaScript series (14) _ javascript skills

Source: Internet
Author: User
In the description of the variable object in Chapter 12th, we already know that the data of the execution context (variables, function declarations, and function parameters) are stored in the variable object as attributes. Preface
In the description of the variable object in Chapter 12th, we already know that the data of the execution context (variables, function declarations, and function parameters) are stored in the variable object as attributes.
At the same time, we also know that the variable object is created every time the context is entered, and the initial value is filled in. Value updates appear in the code execution phase.
This chapter focuses on more details that are directly related to the execution context. This time we will mention an issue-the scope chain.
Http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/.
Http://www.denisdeng.com /? P = 908
Most of the content in this article comes from the above address and only a few modifications are made. Thank you for the author.
Definition
If you want to briefly describe and display the focus, most of the scope chains are related to internal functions.
We know that ECMAScript allows the creation of internal functions, and we can even return these functions from the parent function.

The Code is 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 a global context, it is the global object itself; for a function, it is the activity object.
The scope chain is the list of all variable objects (including parent variable objects) in the internal context. This link is used for variable query. In the preceding example, the scope chains of bar context include AO (bar), AO (foo), and VO (global ).
However, let's take a closer look at this issue.
Let's start with the definition and further discuss the example.
The scope chain is related to an execution context. The variable object chain is used for Variable Search in identifier parsing.
The scope chain of the function context is created when a function is called, including the activity object and the [[scope] attribute inside the function. Next we will discuss in more detail the [[scope] attribute of a function.
The following is a diagram in the context:

The Code is 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 association and identifier parsing process will be discussed below, which is related to the function lifecycle.
Function Lifecycle
The lifecycle of a function can be divided into the creation and activation stages (called). Let's look at it in detail.
Function Creation
As we all know, when entering the context, the function declaration is placed in the variable/activity (VO/AO) object. Let's take a look at the variables and function declarations in the Global Context (here the variable object is the global object itself, we still remember, right ?)

The Code is 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 only talked about variable objects related to the current context. Here, we can see that the variable "y" is defined in the function "foo" (meaning it is in the AO in the foo context), but the variable "x" is not defined in the context of "foo, correspondingly, it will not be added to the AO of "foo. At first glance, the variable "x" does not exist at all relative to the function "foo", but as we can see below-it is just a "glance" and we find that, the activity object in the context of "foo" contains only one attribute-"y ".

The Code is as follows:


FooContext. AO = {
Y: undefined // undefined-20-at activation when the context is entered
};


How does the function "foo" access the variable "x "? Theoretically, the function should be able to access a variable object with a higher context. In fact, this mechanism is implemented through the [[scope] attribute in the function.
[[Scope] is the hierarchical chain of all parent variable objects. It is located in the context of the current function and stored in it when the function is created.
Note this important point-[[scope] is stored when a function is created-static (unchanged), always, forever, until the function is destroyed. That is, the function can never be called, but the [[scope] attribute has been written and stored in the function object.
Another thing to consider is that [[scope] is an attribute of a function rather than context in contrast to the scope chain. Considering the above example, the [[scope] of the function "foo" is as follows:

The Code is as follows:


Foo. [[Scope] = [
GlobalContext. VO // = Global
];


For example, we use the common ECMAScript array to display the scope and [scope].
Continue, we know that the context is entered when the function is called. At this time, the activity object is created, and this and scope (scope chain) are determined. Let's take a closer look at this moment.
Function Activation
As mentioned in the definition, after creating AO/VO In the context, the Scope attribute of the context (a Scope chain of Variable Search) is defined as follows:
Scope = AO | VO + [[Scope]
The code above indicates that the active object is the first object in the scope array, that is, the frontend added to the scope.
Scope = [AO]. concat ([Scope]);
This feature is very important for the processing of identifier parsing.
Identifier Parsing is a processing process used to determine which variable object a variable (or function declaration) belongs.
In the return value of this algorithm, we always have a reference type. Its base component is the corresponding variable object (or null if not found ), the property name component is the name of the identifier to be searched up. Details of the reference type are discussed in Chapter 13th. this.
The identifier parsing process contains the search for the attribute corresponding to the variable name, that is, the continuous search of the variable object in the scope, starting from the deepest context, bypassing the scope chain until the top.
In this way, the local variables in a context have a higher priority than the variables in the parent scope. If two variables have the same name but come from different scopes, the first one is found in the deepest scope.
We use a slightly complex example to describe the above.

The Code is as follows:


Var x = 10;
Function foo (){
Var y = 20;
Function bar (){
Var z = 30;
Alert (x + y + z );
}
Bar ();
}
Foo (); // 60


In this regard, we have the following variables/activity objects, the [[scope] attributes of functions, and the scope chain of context:
The global context variable object is:

The Code is as follows:


GlobalContext. VO === Global = {
X: 10
Foo:
};


When "foo" is created, the [[scope] attribute of "foo" is:

The Code is as follows:


Foo. [[Scope] = [
GlobalContext. VO
];


When "foo" is activated (entering the context), the activity object of the "foo" context is:

The Code is as follows:


FooContext. AO = {
Y: 20,
Bar:
};


The scope chain of the "foo" context is:

The Code is 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:

The Code is as follows:


Bar. [[Scope] = [
FooContext. AO,
GlobalContext. VO
];


When bar is activated, the activity object in the bar context is:

The Code is as follows:


BarContext. AO = {
Z: 30
};


The scope chain of the bar context is:

The Code is as follows:


BarContext. Scope = barContext. AO + bar. [[Scope] // I. e .:
BarContext. Scope = [
BarContext. AO,
FooContext. AO,
GlobalContext. VO
];


The identifiers of "x", "y", and "z" are parsed 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 features
Let's take a look at some important features related to the scope chain and function [scope] attributes.
Closure
In ECMAScript, the closure is directly related to the [[scope] of the function. As we mentioned, [[scope] is stored during function creation and co-survival with the function. In fact, closures are the combination of function code and its [[scope. Therefore, as one of its objects, [[Scope] includes the lexical Scope (parent variable object) created in the function ). When the function is further activated, in the lexical trace of the variable object (static storage for creation), variables from a higher scope will be searched.
For example:

The Code is as follows:


Var x = 10;
Function foo (){
Alert (x );
}
(Function (){
Var x = 20;
Foo (); // 10, but not 20
})();


We can see again that during the identifier parsing process, the lexical scope defined when the function is created -- the variable is parsed to 10, instead of 30. In addition, this example clearly shows that [[scope] of a function (an anonymous function returned from the function "foo" in this example) persists, even after the scope of the function is created.
For more details about the Closure Theory and its execution mechanism in ECMAScript, read chapter 16 closures.
[Scope] of the function created by the constructor
In the preceding example, we can see that the [[scope] attribute of the function is obtained when the function is created, and the variables of all parent contexts are accessed through this attribute. However, this rule has an important exception. It involves functions created through function constructor.

The Code is as follows:


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 ();


We can see that the Function bar created by Function constructor cannot access the variable "y. However, this does not mean that the function "barFn" does not have the [[scope] attribute (otherwise it cannot access the variable "x "). The problem is that the [[scope] attribute of the function created by using the function constructor is always a unique global object. Taking this into consideration, for example, it is impossible to create the context closure at the top of the global layer through this function.
Two-dimensional scope chain search
The most important thing to look for in the scope chain is the attributes of the variable object (if any) which must be taken into account-derived from the prototype features of ECMAScript. If an attribute is not directly found in the object, the query continues in the prototype chain. That is, two-dimensional chain search. (1) Scope chain; (2) each scope chain goes deep into the prototype chain. If attributes are defined in Object. prototype, we can see this effect.

The Code is as follows:


Function foo (){
Alert (x );
}
Object. prototype. x = 10;
Foo (); // 10


The activity object has no prototype. We can see in the following example:

The Code is as follows:


Function foo (){
Var x = 20;
Function bar (){
Alert (x );
}
Bar ();
}
Object. prototype. x = 10;
Foo (); // 20


If the activation Object of the function "bar" context has a prototype, "x" is parsed in Object. prototype because it is not directly parsed in AO. But in the first example above, in the identifier parsing, we reach the global Object (not all in some executions), which is from the Object. prototype is inherited, and "x" is resolved to 10 in response.
The same situation occurs in some versions of the SpiderMokey naming function expressions (NFE), where specific objects are stored from the Object. optional name of the function expression inherited from prototype. In some versions of Blackberry, the Object is activated from the Object during execution. prototype inheritance. However, more details about this feature are discussed in Chapter 15th functions.
Scope chain in global and eval Context
This is not necessarily interesting, but it must be noted. The scope chain of the global context only contains global objects. The context of the Code eval has the same scope chain as the current call context.

The Code is as follows:


GlobalContext. Scope = [
Global
];
EvalContext. Scope === callingContext. Scope;


Impact 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 frontend of the scope chain, and objects must be searched in the identifiers that appear in these declarations. If one of them occurs, the scope chain is briefly modified as follows:
Scope = withObject | catchObject + AO | VO + [Scope]
In this example, an object is added, and the object is its parameter (in this way, the attribute of this object becomes accessible without a prefix ).

The Code is as follows:


Var foo = {x: 10, y: 20 };
With (foo ){
Alert (x); // 10
Alert (y); // 20
}


Modify the scope chain to the following:
Scope = foo + AO | VO + [[Scope]
We can see again that through the with statement, the identifier parsing in the object is added to the frontend of the scope chain:

The Code is as follows:


Var x = 10, y = 10;
With ({x: 20 }){
Var x = 30, y = 30;
Alert (x); // 30
Alert (y); // 30
}
Alert (x); // 10
Alert (y); // 30


What happens when the context is entered? The identifiers "x" and "y" have been added to the variable object. In addition, make the following changes in the Code running phase:
X = 10, y = 10;
Object {x: 20} added to the frontend of the scope;
In with, a var declaration is encountered, and nothing is created, because when the context is entered, all variables have been parsed and added;
In step 2, only the variable "x" is modified. In fact, "x" in the object is resolved and added to the frontend of the scope chain. "x" is 20 and changed to 30;
There are also modifications to the variable object "y". After being parsed, its value also 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 is restored to the State before the with is enhanced.
In the last two alert statements, the "x" of the current variable object remains the same, and the "y" value is now equal to 30, which has changed in the with declaration runtime.
Similarly, the exception parameter of the catch statement becomes accessible, and it creates a new object with only one attribute-the exception parameter name. The graph looks like this:

The Code is as follows:


Try {
...
} Catch (ex ){
Alert (ex );
}


Modify the scope chain:

The Code is as follows:


Var catchObject = {
Ex:
};
Scope = catchObject + AO | VO + [[Scope]


After the catch statement is run, the scope chain is restored to the previous state.
Conclusion
At this stage, we have considered almost all common concepts related to the execution context and their details. According to the plan-detailed analysis of function objects: function types (function declaration, 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 am happy to answer your questions in the comments.
Other references
  • 8.6.2-[[Scope]
  • 10.1.4-Scope Chain and Identifier Resolution
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.