Copy codeThe Code is as follows:
Var classA = function (){
This. prop1 = 1;
}
ClassA. prototype. func1 = function (){
Var that = this,
Var1 = 2;
Function (){
Return function (){
Alert (var1 );
Alert (this. prop1 );
}. Apply (that );
};
A ();
}
Var objA = new ClassA ();
ObjA. func1 ();
You should write a similar code. In fact, what I want to express here is that sometimes a method definition is 108,000 miles apart from where it is used. When the method is executed, what variables can be accessed and what variables cannot be accessed? How can this problem be determined? This is the problem we need to analyze this time-lexical Scope
Lexical scope: the scope of a variable is determined during definition rather than execution. That is to say, the lexical scope depends on the source code and can be determined through static analysis. Therefore, the lexical scope is also called static scope. Except with and eval, we can only say that the JS scope mechanism is very close to the Lexical scope ).
The following are a few small cases to get an in-depth understanding of lexical scopes and closures. Some Concepts and theoretical knowledge at the underlying layer during Javascript execution.
Classic case column Reproduction
1. Classic Case 1
Copy codeThe Code is as follows:
/* A piece of code in the global (window) Field */
Function a (I ){
Var I;
Alert (I );
};
A (10 );
Q: What will the above Code output?
Answer: Yes, 10 is displayed. The specific execution process should be like this
Function a has an I parameter. When function a is called, input 10 arguments. The I parameter is 10.
Next, define a local variable I with the same name without assigning a value.
Alert output 10
Thinking: is the local variable I and the shape parameter I the same bucket?
2. Classic Case 2
Copy codeThe Code is as follows:
/* A piece of code in the global (window) Field */
Function a (I ){
Alert (I );
Alert (arguments [0]); // arguments [0] should be the form parameter I
Var I = 2;
Alert (I );
Alert (arguments [0]);
};
A (10 );
Q: What will the above Code output? (10, 10, 10 ))
Answer: The running result in FireBug is the second 10, 10, 2, 2, right... The following describes the specific execution process.
Function a has an I parameter. When function a is called, input 10 arguments. The I parameter is 10.
The first alert outputs the value of I 10.
The second alert outputs arguments [0], which should also be I
Then define a local variable I and assign it to 2. At this time, the local variable I = 2
The third alert outputs the value 2 of the local variable I.
The fourth alert outputs arguments [0] again.
Thinking: Can the local variable I and the parameter I have the same value?
3. Typical Case 3
Copy codeThe Code is as follows:
/* A piece of code in the global (window) Field */
Function a (I ){
Var I = I;
Alert (I );
};
A (10 );
Q: What will the above Code output? (Undefined 10 ))
Answer: The running result in FireBug is 10. The specific execution process is briefly described below.
The first clause declares a local variable with the same name as the form parameter I. According to the result, we know that the next I point
This is equivalent to assigning the value 10 of the parameter I to the local variable I.
The second alert will certainly output 10
Thinking: Combined with Case 2, here we can basically show that the local variable I and the form parameter I point to the same storage address!
4. Typical Case 4
Copy codeThe Code is as follows:
/* A piece of code in the global (window) Field */
Var I = 10;
Function (){
Alert (I );
Var I = 2;
Alert (I );
};
A ();
Q: What will the above Code output? (Boy, you're not dead this time! Wow Haha, I will not give you the option)
Answer: The running result in FireBug is undefined, 2. The specific execution process is briefly described below.
The first alert outputs undefined
The second alert output 2
Think: What's the problem?
5. Typical Case 5 .............. N
When you see the above examples, you may think about how it is possible that I have written JavaScript for a few years, and I will hesitate to give such a simple example, and the result may be wrong. In fact, the possible reason is: we can quickly write a method, but in the end, how is the internal implementation of the method? What are the execution details? You may not have any in-depth study or understanding. To understand these details, we need to understand how the JS engine works. So we will give a slightly more in-depth introduction to the parsing process of a method by the JS engine.
Parsing process
1. execution sequence
- The compilation steps include lexical analysis, syntax analysis, semantic check, code optimization, and byte generation.
- After obtaining the syntax analysis tree through lexical analysis and syntax analysis, the interpreted language can start to be executed. Here is a simple and original principle of the parsing process. It is only for reference. The detailed parsing process (different JS engines) also requires further research.
JavaScript execution process. If a Document Stream contains multiple script code segments (JavaScript code separated by the script tag or the introduced js file), the running sequence is as follows:
- Step 1. Read the first code segment (the js execution engine does not execute the program in one row, but analyzes and executes the program in one row)
- Step 2. Perform lexical analysis and syntax analysis. If there is a mistake, the system reports a syntax error (such as mismatched brackets) and jumps to Step 5.
- Step 3. Perform "pre-resolution" on the [var] variable and [function] definition (no error will be reported because only the correct declaration is parsed)
- Step 4. Execute the code segment. If there is an error, an error is returned (for example, the variable is undefined)
- Step 5. If there is another code segment, read the next code segment and repeat Step 2.
- Step 6. End
2. Special Instructions
All JS code in the window field can be regarded as an "anonymous method" and will be automatically executed, other methods in this "anonymous method" are executed only when they are displayed and called.
3. Key Steps
The above process is divided into two phases.
- Parsing: A Legal syntax analysis tree is constructed through syntax analysis and pre-resolution.
- Execute: execute a specific function. When the JS engine executes each function instance, it creates an execution environment (ExecutionContext) and an activity object (activeObject) (they belong to the Host object, is consistent with the life cycle of the function instance)
3. Key Concepts
Here, we will emphasize the following concepts, which will be represented by one entity below for your understanding.
- SyntaxTree intuitively shows information about this Code. The JS engine creates tables to record the variables in each method ), method set (functions) and scope (scope)
- The execution environment (ExecutionContext) can be understood as an object that records the current method [external description information], the type, name, parameter, and activity object of the method to be executed)
- ActiveObject can be understood as an object that records the internal execution information of the current method, and records the internal variables and nested functions), real parameters (arguments), scope chain (scopeChain) and other required information for execution, including the internal variable set (variables), nested function set (functions) is directly copied from the syntax analysis tree created in step 1.
- Lexical scope: the scope of a variable is determined during definition rather than execution. That is to say, the lexical scope depends on the source code and can be determined through static analysis. Therefore, the lexical scope is also called static scope. Except with and eval, we can only say that the JS scope mechanism is very close to the Lexical scope)
- Scope chain: the implementation mechanism of lexical scopes is scopeChain ). The scope chain is a set of Name Lookup mechanisms. First, it is searched in the ActiveObject of the current execution environment. If it is not found, it is searched in the parent ActiveObject along the scope chain, always find the Global Object)
4. entity Representation
Resolution Simulation
It is estimated that everyone is still confused here. What is the syntax analysis tree? What is the syntax analysis tree like? How is the scope chain implemented? What is the content of the activity object, still not very clear. Next we will simulate the entire parsing process through a piece of actual code. We will create the syntax analysis tree and activity objects to understand the scope, how is the scope chain implemented?
1. Simulate code
Copy codeThe Code is as follows:
/* A piece of code in the global (window) Field */
Var I = 1, j = 2, k = 3;
Function a (o, p, x, q ){
Var x = 4;
Alert (I );
Function B (r, s ){
Var I = 11, y = 5;
Alert (I );
Function c (t ){
Var z = 6;
Alert (I );
};
// Function expression
Var d = function (){
Alert (y );
};
C (60 );
D ();
};
B (40, 50 );
}
A (10, 20, 30 );
2. syntax analysis tree
The above code is very simple, that is, some global variables and global methods are defined first, and then local variables and local methods are defined in the method. Now the JS interpreter reads this code and starts parsing, as mentioned above, the JS engine will first obtain the syntax analysis tree through syntax analysis and pre-resolution. As for what the syntax analysis tree looks like, there will be some information. Below we will use a simple structure: a js object (to clearly express the reference relationship between various objects, here it is only a pseudo Object Representation and may not be able to run) to describe the syntax analysis tree (which we are familiar, we will not go into the actual structure, but it must be much more complicated. Here we want to simplify the parsing process to help you understand it)
Copy codeThe Code is as follows:
/**
* Simulate the creation of a syntax analysis tree and store the variables and methods in the function.
*/
Var SyntaxTree = {
// Global Object Representation in the syntax analysis tree
Window :{
Variables :{
I: {value: 1 },
J: {value: 2 },
K: {value: 3}
},
Functions :{
A: this.
}
},
A :{
Variables :{
X: "undefined"
},
Functions :{
B: this. B
},
Scope: this. window
},
B :{
Variables :{
Y: "undefined"
},
Functions :{
C: this. c,
D: this. d
},
Scope: this.
},
C :{
Variables :{
Z: "undefined"
},
Functions :{},
Scope: this. B
},
D :{
Variables :{},
Functions :{},
Scope :{
Myname: d,
Scope: this. B
}
}
};
The above is a simple representation of the syntax analysis tree. As we analyzed earlier, the syntax analysis tree mainly records the variables and functions in each function) and scope)
Key points of the syntax analysis tree
1. In a variables set, only variables are defined and no variable values exist. At this time, all the variable values are "undefined"
Scope, according to the characteristics of the lexical scope, the scope of each variable is clearly defined at this time, and will not change with the execution environment. [What do you mean? That is, we often return a method back and execute it in another method. During execution, the scope of variables in the method follows the scope defined by the method. In fact, what I want to express here is that no matter how complicated and far you are, You can execute this method to determine whether the variables in the method can be accessed or you have to go back to the method definition for verification]
3. Create a scope rule
A For the function declaration and anonymous function expression, [scope] is the scope of its creation.
B. For function expressions with names, the top of [scope] is a new JS Object (that is, inheriting the Object. prototype), this object has two attributes, the first is its own name, the second is the defined scope, the first function name is used to ensure that the code inside the function can access its own function name without error for recursion.
3. Execution Environment and activity objects
After the syntax analysis is complete, run the code. When we call every method, the JS engine automatically creates an execution environment and an activity object for it. They are consistent with the life cycle of the Method Instance, provide necessary execution support for method execution. For the above methods, we have set up activity objects for them in a unified way (the activity object is generated only when the method is executed, for ease of demonstration, the activity objects of all methods are defined here), as follows:
Execution Environment
Copy codeThe Code is as follows:
/**
* Execution environment: the execution environment created during function execution
*/
Var ExecutionContext = {
Window :{
Type: "global ",
Name: "global ",
Body: ActiveObject. window
},
A :{
Type: "function ",
Name: "",
Body: ActiveObject.,
ScopeChain: this. window. body
},
B :{
Type: "function ",
Name: "B ",
Body: ActiveObject. B,
ScopeChain: this. a. body
},
C :{
Type: "function ",
Name: "c ",
Body: ActiveObject. c,
ScopeChain: this. B. body
},
D :{
Type: "function ",
Name: "d ",
Body: ActiveObject. d,
ScopeChain: this. B. body
}
}
The execution environment of each method above stores information such as the corresponding method type (function), Method Name (funcName), activity object (ActiveObject), and scope chain (scopeChain, the key points are as follows:
Body property, direct to the activity object of the current method
ScopeChain attribute and scope chain. It is a linked list structure. According to the scope attribute corresponding to the current method in the syntax analysis tree, it points to the activity object (ActivceObject) of the method corresponding to the scope ), variable Search follows this chain.
Activity object
Copy codeThe Code is as follows:
/**
* Activity object: List of activity objects created during function execution
*/
Var ActiveObject = {
Window :{
Variables :{
I: {value: 1 },
J: {value: 2 },
K: {value: 3}
},
Functions :{
A: this.
}
},
A :{
Variables :{
X: {value: 4}
},
Functions :{
B: SyntaxTree. B
},
Parameters :{
O: {value: 10 },
P: {value: 20 },
X: this. variables. x,
Q: "undefined"
},
Arguments: [this. parameters. o, this. parameters. p, this. parameters. x]
},
B :{
Variables :{
Y: {value: 5}
},
Functions :{
C: SyntaxTree. c,
D: SyntaxTree. d
},
Parameters :{
R: {value: 40 },
S: {value: 50}
},
Arguments: [this. parameters. r, this. parameters. s]
},
C :{
Variables :{
Z: {value: 6}
},
Functions :{},
Parameters :{
U: {value: 70}
},
Arguments: [this. parameters. u]
},
D :{
Variables :{},
Functions :{},
Parameters :{},
Arguments: []
}
}
Each of the above activity objects stores the required information for executing the corresponding method, such as the internal variable set (variables), nested function set (functions), parameter (parameters), and real parameter (arguments, key Points of activity objects
Create an activity object, and copy the internal variable set (variables) and nested function set (functions) of the method from the syntax analysis tree)
Method, and all the internal variable sets in the activity object are reset to undefined.
Create a parameters object and an arguments object with the same name as a real parameter. The reference relationship is between the parameter and the variable.
Execute the value assignment statement in the method to assign values to the variables in the variable set.
The Variable Search Rule is first searched in the ActiveObject of the current execution environment. If it is not found, it is searched in the ActiveObject pointed to by the attribute ScopeChain in the execution environment until the Global Object (window)
After the method is executed, the internal variable value is not reset. For how to destroy the variable, see the following
The life cycle of the variable in the method depends on whether the Method Instance has active reference. If not, the active object is destroyed.
6 and 7 are the root causes for the closure to access external variables.
Reinterpreting classic cases
Case 1, 2, 3
According to [in a method, a real parameter with the same name is referenced between the form parameter and the variable, that is, the processing of the JS engine is a variable with the same name and the form parameter both reference the same memory address ], therefore, the change of arguments in No. 2 will affect local variables.
Case 4
Based on the JS Engine Variable Search rules, search for them in the ActiveObject of the current execution environment. If not, search for them in the ActiveObject indicated by the ScopeChain attribute in the execution environment until the Global Object (window) because the definition of variable I is found in the current ActiveObject, but the value is "undefined", the output is "undefined ".
Summary
The above is what I learned and used JS for a while, in order to have a better understanding of it, but also to better grasp its application, so that in the process of learning the closure, I have some understanding and summary of the lexical scopes. There may be some differences between them and the actual JS interpretation engine, because I just analyzed this problem from the perspective of a beginner front-end developer rather than the system designer, hoping to help JavaScript developers understand the scope of this method!