During JavaScript programming, we can't avoid declaring functions and variables to build our system successfully. But how and where can the interpreter find these functions and variables? What happened when we reference these objects?
Original release: Dmitry A. Soshnikov
Release date:
Russian: http://dmitrysoshnikov.com/ecmascript/ru-chapter-2-variable-object/
Dmitry A. Soshnikov
Release date: 2010-03-15
Http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/
Some sentences that are difficult to translate refer to justinw's Chinese translation.
Most ECMAScript programmers should know that variables are closely related to execution context:
Copy codeThe Code is as follows:
Var a = 10; // a variable in the Global Context
(Function (){
Var B = 20; // The local variable in the function Context
})();
Alert (a); // 10
Alert (B); // The global variable "B" is not declared
In addition, many programmers know that the current ECMAScript specification specifies that the independent scope can only be created through the execution context of the "function" code type. That is to say, compared with C/C ++, The for loop in ECMAScript cannot create a local context.
Copy codeThe Code is as follows:
For (var k in {a: 1, B: 2 }){
Alert (k );
}
Alert (k); // although the loop has ended, the variable k is still in the current scope
Let's take a look at the details we found when declaring the data.
Data Declaration
If the variable is related to the execution context, the variable should know where its data is stored and how to access it. This mechanism is called a variable object ).
A variable object (VO) is a special object related to the execution context. It stores the following content declared in the context:
Variable (var, variable Declaration );
Function declaration (FunctionDeclaration );
Function Parameters
For example, we can use a common ECMAScript object to represent a variable object:
Copy codeThe Code is as follows:
VO = {};
As we said, VO is the property of the execution context ):
ActiveExecutionContext = {
VO :{
// Context Data (var, FD, function arguments)
}
};
Only the variable objects in the global context can be indirectly accessed through the VO attribute name (because in the global context, the global object itself is a variable object, which will be detailed later ), in other contexts, you cannot directly access the VO object because it is only an implementation of the internal mechanism.
When we declare a variable or function, it is no different from when we create a new VO attribute (that is, there are names and corresponding values ).
For example:
Copy codeThe Code is as follows:
Var a = 10;
Function test (x ){
Var B = 20;
};
Test (30 );
The corresponding variable object is:
Copy codeThe Code is as follows:
// Global context variable object
VO (globalContext) = {
A: 10,
Test: <reference to function>
};
// Variable object of the test function Context
VO (test functionContext) = {
X: 30,
B: 20
};
Variable objects are just an abstract concept at the specific implementation level (and in specifications. (Essentially, in the context of specific execution, the VO name is different and the initial structure is different.
Variable objects in different execution contexts
For all types of execution contexts, some operations (such as variable initialization) and actions of the variable object are common. From this perspective, it is easier to understand the variable object as an abstract basic thing. The additional content related to the variable object is also defined in the function context.
Copy codeThe Code is as follows:
Abstract variable object VO (common behavior during variable initialization)
Bytes
GlobalContextVO
Export (VO === this === global)
Bytes
Function context variable object FunctionContextVO
(VO = AO, and <arguments> and <formal parameters> are added)
Let's take a closer look:
Variable objects in the Global Context
First, we need to give a clear definition to the global object:
A Global object is an object created before any execution context is entered;
This object only has one copy, and its attributes can be accessed anywhere in the program. The life cycle of the global object ends at the moment when the program exits.
Copy code
The Math, String, Date, and parseInt attributes of the global object are initialized in the initial creation phase, you can also create additional objects as attributes (which can point to the global object itself ). For example, in DOM, the window attribute of the global object can reference the global object itself (Of course, not all implementations are like this ):
Copy codeThe Code is as follows:
Global = {
Math: <...>,
String: <...>
...
...
Window: global // reference itself
};
Prefix is often ignored when accessing global object attributes, because global objects cannot be accessed directly by name. However, we can still access global objects through this in the global context. We can also reference ourselves recursively. For example, window in DOM. To sum up, the code can be abbreviated:
Copy codeThe Code is as follows:
String (10); // global. String (10 );
// With a prefix
Window. a = 10; // = global. window. a = 10 = global. a = 10;
This. B = 20; // global. B = 20;
Therefore, return to the variable object in the Global Context -- here, the variable object is the global object itself:
VO (globalContext) = global;
It is very necessary to understand the above conclusion. Based on this principle, we can access it indirectly through the attributes of the global object in the global context. (For example, do not know the variable name beforehand ).
Copy codeThe Code is as follows:
Var a = new String ('test ');
Alert (a); // for direct access, find "test" in VO (globalContext"
Alert (window ['a']); // access via global indirectly: global === VO (globalContext): "test"
Alert (a === this. a); // true
Var aKey = 'a ';
Alert (window [aKey]); // access by dynamic attribute name indirectly: "test"
Variable objects in the function Context
In the context of function execution, VO cannot be directly accessed. At this time, the active object (AO) plays the role of VO.
VO (functionContext) = AO;
An active object is created when the context of the function is entered. It is initialized through the arguments attribute of the function. The value of the arguments attribute is an Arguments object:
Copy codeThe Code is as follows:
AO = {
Arguments: <ArgO>
};
An Arguments object is an attribute of an activity object. It includes the following attributes:
Callee-reference to the current function
Length-Number of actually passed parameters
The properties-indexes (an integer of the string type) attribute value is the parameter value of the function (arranged from left to right by the parameter list ). The number of elements in properties-indexes is equal to that in arguments. length. properties-indexes and the actually passed parameters are shared.
For example:
Copy codeThe Code is as follows:
Function foo (x, y, z ){
// Number of declared function parameters arguments (x, y, z)
Alert (foo. length); // 3
// The number of actually passed parameters (only x, y)
Alert (arguments. length); // 2
// The callee parameter is the function itself
Alert (arguments. callee = foo); // true
// Parameter sharing
Alert (x = arguments [0]); // true
Alert (x); // 10
Arguments [0] = 20;
Alert (x); // 20
X = 30;
Alert (arguments [0]); // 30
// However, the input parameter z is not shared with the parameter's 3rd index values.
Z = 40;
Alert (arguments [2]); // undefined
Arguments [2] = 50;
Alert (z); // 40
}
Foo (10, 20 );
The code in this example has a bug in the current Google Chrome browser-even if the z, z, and arguments [2] parameters are not passed, they are still shared.
Two phases of processing the context code
Now we have reached the core point of this article. The execution context code is divided into two basic stages for processing:
Enter execution Context
Execute Code
Changes to variable objects are closely related to these two phases.
Note: The processing of these two phases is a general action and is irrelevant to the context type (that is, the global context and function context are the same ).
Enter execution Context
When you enter the execution context (before code execution), VO contains the following attributes (as mentioned earlier ):
All parameters of the function (if we are in the context of function execution)
-The property of a variable object consisting of a name and a corresponding value is created. If no corresponding parameter is passed, the attributes of a variable object consisting of the name and undefined value will also be created.
All function declarations (FunctionDeclaration, FD)
-The property of a variable object consisting of a name and a corresponding value (function object) is created. If the variable object already has an attribute with the same name, the property is completely replaced.
All variable declarations (var, VariableDeclaration)
-Attributes of a variable object composed of the name and the corresponding value (undefined) are created. If the variable name is the same as the declared parameters or functions, the variable declaration does not interfere with the existing attributes.
Let's take an example:
Copy codeThe Code is as follows:
Function test (a, B ){
Var c = 10;
Function d (){}
Var e = function _ e (){};
(Function x (){});
}
Test (10); // call
When entering the context of the test function with the parameter 10, AO performs as follows:
Copy codeThe Code is as follows:
AO (test) = {
A: 10,
B: undefined,
C: undefined,
D: <reference to FunctionDeclaration "d">
E: undefined
};
Note that AO does not contain the function "x ". This is because "x" is a function expression (FunctionExpression, abbreviated as FE) rather than a function declaration. function expressions do not affect VO. In any case, the function "_ e" is also a function expression, but as we will see below, because it is assigned to the variable "e ", therefore, it can be accessed by the name "e. The difference between the function declaration FunctionDeclaration and the function expression FunctionExpression will be discussed in detail in chapter 15th Functions. You can also refer to Chapter 2nd of this series to reveal the name of the function expression.
After that, the second stage of processing the context code is executed.
Code Execution
In this period, AO/VO already has attributes (however, not all attributes have values, and most attribute values are still the default undefined values ).
In the previous example, AO/VO was modified as follows during code interpretation:
Copy codeThe Code is as follows:
AO ['C'] = 10;
AO ['E'] = <reference to FunctionExpression "_ e">;
Note that since FunctionExpression "_ e" is saved to the declared variable "e", it still exists in the memory. FunctionExpression "x" does not exist in AO/VO. That is to say, if we want to call the "x" function, no matter before or after the function is defined, an error "x is not defined" will occur. unsaved function expressions can be called only in their own definitions or recursion.
Another typical example:
Copy codeThe Code is as follows:
Alert (x); // function
Var x = 10;
Alert (x); // 10
X = 20;
Function x (){};
Alert (x); // 20
Why does the first alert "x" return a function, and it still accesses "x" before "x" is declared? Why not 10 or 20? Because, according to the canonicalized function declaration is filled in when the context is entered; the consent period, when the context is entered, there is a variable Declaration "x", as we said in the previous stage, variable declaration follows the function declaration and formal parameter declaration in sequence, and enters the context stage, the variable declaration does not interfere with the existing function declaration of the same name or formal parameter declaration in VO. Therefore, when entering the context, the VO structure is as follows:
Copy codeThe Code is as follows:
VO = {};
VO ['X'] = <reference to FunctionDeclaration "x">
// Find var x = 10;
// If function "x" is not declared
// At this time, the value of "x" should be undefined.
// But the variable declaration in this case does not affect the function value of the same name.
VO ['X'] = <the value is not disturbed, still function>
Then, in the code execution stage, VO makes the following changes:
Copy codeThe Code is as follows:
VO ['X'] = 10;
VO ['X'] = 20;
We can see this effect in the second and third alert.
In the following example, we can see that the variables are put into VO at the context stage. (Because, although some else code will never be executed, the variable "B" still exists in VO .)
Copy codeThe Code is as follows:
If (true ){
Var a = 1;
} Else {
Var B = 2;
}
Alert (a); // 1
Alert (B); // undefined. The value of B is not declared.
About Variables
Generally, various articles and JavaScript-related books claim that "a variable can be declared whether the var keyword is used (in the Global Context) or the var keyword is not used (anywhere ". Remember, this is an incorrect concept:
Variables can only be declared by using the var keyword at any time.
The above assignment statement:
A = 10;
This only creates a new attribute for the Global Object (but it is not a variable ). "Not a variable" does not mean that it cannot be changed, but does not comply with the variable concept in the ECMAScript specification, so it is "not a variable" (the reason why it can become a global object attribute, this is because VO (globalContext) = global. Do you still remember this ?).
Let's take a look at the specific differences through the following examples:
Copy codeThe Code is as follows:
Alert (a); // undefined
Alert (B); // "B" is not declared
B = 10;
Var a = 20;
The root cause is still VO and enters the context and code execution phases:
Enter the context stage:
Copy codeThe Code is as follows:
VO = {
A: undefined
};
We can see that "B" is not a variable, so there is no "B" at this stage ", "B" will only appear in the code execution phase (but in our example, an error has occurred before it reaches ).
Let's change the example code:
Copy codeThe Code is as follows:
Alert (a); // undefined, everyone knows,
B = 10;
Alert (B); // 10, code execution stage Creation
Var a = 20;
Alert (a); // 20, code execution stage Modification
There is also an important knowledge point about variables. Compared with a simple attribute, a variable has a feature (attribute): {DontDelete}. This feature means that the delete operator cannot be used to directly delete the variable attribute.
Copy codeThe Code is as follows:
A = 10;
Alert (window. a); // 10
Alert (delete a); // true
Alert (window. a); // undefined
Var B = 20;
Alert (window. B); // 20
Alert (delete B); // false
Alert (window. B); // still 20
However, this rule does not have to go out of context, that is, the eval context. The variable does not have the {DontDelete} feature.
Copy codeThe Code is as follows:
Eval ('var a = 10 ;');
Alert (window. a); // 10
Alert (delete a); // true
Alert (window. a); // undefined
When you use a debugging tool (for example, Firebug) to test the instance, you must note that Firebug also uses eval to execute your code on the console. Therefore, the variable property does not have the {DontDelete} feature and can be deleted.
Special implementation: _ parent _ attribute
As mentioned above, according to the standard, activity objects cannot be directly accessed. However, some specific implementations do not fully comply with this provision. For example, in the implementation of SpiderMonkey and Rhino;, the function has a special attribute _ parent __, this attribute can be used to directly reference the activity object or global variable object created by the function.
For example (SpiderMonkey, Rhino ):
Copy codeThe Code is as follows:
Var global = this;
Var a = 10;
Function foo (){}
Alert (foo. _ parent _); // global
Var VO = foo. _ parent __;
Alert (VO. a); // 10
Alert (VO = global); // true
In the above example, we can see that the function foo is created in the global context, so the attribute _ parent _ points to the global context variable object, that is, the global object.
However, it is impossible to access the active object in the same way in SpiderMonkey: in different versions of SpiderMonkey, the _ parent _ of the internal function sometimes points to null and sometimes to global objects.
In Rhino, you can access activity objects in the same way.
For example (Rhino ):
Copy codeThe Code is as follows:
Var global = this;
Var x = 10;
(Function foo (){
Var y = 20;
// Activity object in the context of "foo"
Var AO = (function () {}). _ parent __;
Print (AO. y); // 20
// The _ parent _ of the current active object is an existing global object.
// The Special chain of the variable object is
// So we call it the scope chain.
Print (AO. _ parent _ = global); // true
Print (AO. _ parent _. x); // 10
})();
Summary
In this article, we have learned in depth the objects related to the execution context. I hope this knowledge can help you solve some problems or confusions you have encountered. As planned, in subsequent chapters, we will explore the scope chain, identifier resolution, and closure.
If you have any questions, I am very happy to help you answer them in the following comments.
Other references
- 10.1.3-Variable Instantiation;
- 10.1.5-Global Object;
- 10.1.6-Activation Object;
- 10.1.8-Arguments Object.