Execution context
Series of the opening
Establish clear, precise, essential concepts and clear, precise, necessary connections between them as you enter the front end, so that you can stay calm in whatever interview you’re going to be doing. There is no catalog, but a web of knowledge formed through the association of concepts, as you can see below. When you encounter a relation concept, you can use the parentheses (strong/weak) to determine whether the relation is strongly related to the concept you are understanding (you need to understand before you can proceed) or weakly related (knowledge expansion) to improve your reading efficiency. I also update related concepts regularly.
The interview questions
- What is an execution context?
- When the JAVASCRIPT engine processes a piece of script content, in what order is it parsed and executed?
- What are the lexical environments associated with it, this pointing to, global execution context, variable objects, etc.?
What’s this for?
When the engine parses executable code, it creates an “execution context” or an execution environment. It is an abstraction of the code execution environment.
And scope
And scope [relevance concept (strong)] sound similar, but don’t get confused, it’s different.
The biggest difference is simply that the lexical scope rules mentioned earlier are fixed at code definition and cannot be changed.
The execution context is an environment that can be changed during code execution.
For example, if you define a function, its scope is defined when the function is defined, but the execution context is created before the function is called.
Execution context stack
We create a new execution context every time we call a function, so how do we manage the execution context created by such a complex call?
So you have an execution context stack, or call stack, execution context stack, ECS, whatever it is. This concept sounds familiar, but if you’ve heard a lot about this, analyze the call stack by analyzing this.
Stack [data structure], having LIFO (last in first out) structure. We use it to store all execution contexts created during code execution.
Global execution context
First we introduce the next special execution context, the global execution context. There is only one unique global context in a program.
When JavaScript begins to interpret execution code, the first thing it encounters is global code, so it pushes a global execution context onto the execution context stack, which remains at the bottom of the stack for the entire application life and is emptied until the entire application is finished.
Function execution context
Each time we call a function, a new execution context is created. Generally we are talking about function execution context.
The simulation process
For a quick simulation, define an array ExecutionContextStack = [] to represent the ExecutionContextStack.
First, the global execution context is pushed.
GlobalContext represents the global execution context
ExecutionContextStack = [
globalContext
];
Copy the code
And then we go to this code
function inner() {
console.log('k')}function outer() {
inner();
}
outer();
Copy the code
So presumably the pseudo-code for the simulation should be
// Function execution context representation
/ / call the outer ();
ExecutionContextStack.push(<outer> functionContext);
// inner is called from outer, and the execution context of inner is created
ExecutionContextStack.push(<inner> functionContext);
// inner completes execution
ExecutionContextStack.pop();
// outer completes execution
ExecutionContextStack.pop();
// Javascript then executes the following code to create a new execution context for the new function
Copy the code
General process diagram
The execution context into the global context into the outer function stack inner function context stack | | | | | inner functionContext | | | - > | outer functionContext | - > | outer FunctionContext | - > | globalContext | | globalContext | | globalContext | inner performed a complete the outer stack | | | | continue to execute the following code, But the bottom of the stack forever | outer functionContext | - > | | - > far a globalContext until the entire | globalContext | | globalContext | end application execution, stack is emptyCopy the code
Performs the context creation process
Why do we need to understand the creation process when we are essentially breaking down the structure of the execution context
It’s sort of divided into these parts
- Determine how this binds (this Binding)
- Lexical Environment
- Variable Environment
It looks like this
// Execute context concept pseudocode
ExecutionContext = {
ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }},Copy the code
This Binding
In simple terms, in the context of global execution, the value of this refers to the global object.
In the context of function execution, the value of this depends on how the function is called, which I won’t go into here. See this related Concepts (strong) concepts topic.
Lexical Environment
In JavaScript, for every function run, the code block {… }, as well as the whole script, has an internal (hidden) correlation object called the Lexical Environment.
This is a Specification object: it’s just an object that exists “theoretically” in a programming language specification to describe how things work. We can’t get this object in code and manipulate it directly.
The lexical environment object consists of two parts:
- Environment Record – an object that stores all local variables as its properties (including some other information, such as the value of this).
- A reference to an external lexical environment that is associated with external code (with access to the external lexical environment).
So a “variable” is just a property of the environment’s record of this particular internal object. “Get or modify a variable” means “get or modify a property of the lexical environment.”
For example,
The following example comes from a modern JavaScript tutorial and is clearly linked at the end.
There is only one lexical environment in this simple code with no functions:
This is known as the global lexical environment associated with the entire script.
In the image above, the rectangle represents the environment record (variable store) and the arrow represents the external reference.
The global lexical environment has no external references, so the arrow points to NULL.
As the code starts and continues to run, the lexical environment changes. Here’s the longer code:
The rectangle on the right illustrates how the global lexical environment changes during execution:
- When the script starts running, the lexical environmentprefilltheAll declared variables.
- Initially, they are in the “Uninitialized” state. This is a special internal state, which means that the engine knows about the variable, but cannot refer to it until it is declared with let. It’s almost as if the variable doesn’t exist.
- Then the let phrase definition appears. It has not been assigned, so its value is undefined. From this point on, we can use variables.
- Phrase is given a value.
- The value of phrase has been modified.
And then we’re going to look at function declarations
A function is also a value, just like a variable.
The difference is that the initialization of the function declaration is done immediately. Remember, this is the principle of variable promotion -> function first.
When a Lexical Environment is created, function declarations immediately become out-of-the-box functions (unlike lets, which are not available until the declaration).
This is why we can call a function declaration before its definition.
But this behavior only applies to function declarations, not function expressions where we assign functions to variables, such as let say = function(name) {… }
Let’s look at the relationship between internal and external lexical contexts
First of all, there are internal functions, indicating that there is a function execution, in the global or function internal lexical environment and create internal function environment, say scope will not be more clear, in fact, here is to talk about the principle of scope chain.
When a function runs, at the beginning of the call, a new lexical environment is automatically created to store the local variables and arguments of the call.
For example, for say(“John”), it looks like this (the current execution position is on the line marked by the arrow) :
During this function call, we have two lexical environments: internal (for function calls) and external (global) :
- Internal lexical environment and
say
Corresponds to the current execution of. It has a single property:name
, arguments to the function. What we’re calling is thetasay("John")
, soname
The value of"John"
. - The external lexical environment is the global lexical environment. It has a
phrase
Variables and functions themselves.
The inner lexical environment references OUTER.
When code accesses a variable — it searches the internal lexical context, then the external context, then the more external context, and so on, all the way to the global lexical context.
If the variable is not found anywhere, an error is reported in strict mode (in non-strict mode, assigning a value to an undefined variable creates a global variable for downward compatibility).
In this example, the search is as follows:
- for
name
Variables, whensay
In thealert
Try to accessname
Will be immediately inInternal lexical environmentFind it. - When it tries to access
phrase
While the interior does notphrase
So it goes alongReferences to the external lexical environmentI found it.
Do you now have a clear idea of how scope chains are formed and why internal scopes can access external variables?
Look at this example, when a function returns a function:
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
Copy the code
At the beginning of each makeCounter() call, a new lexical environment object is created to store the variables of the makeCounter runtime. Thus, we have two levels of nested lexical environment, as in the example above :(note that we are only running to the red triangle now)
The difference is that during makeCounter() a nested function is created that is only one line long: return count++. We haven’t run it yet, we just created it.
All functions are “born” to “remember” the lexical context in which they were created. Technically, there is no magic:All the functionsAre famous for[[Environment]] 的Hidden attributeThe attributeSaves a reference to the lexical environment in which the function was created.
Therefore, counter.[[Environment]] has references to the {count: 0} lexical Environment. This is how a function remembers where it was created, regardless of where the function was called. The [[Environment]] reference is set and stored permanently at function creation time.
Later, when counter() is called, a new lexical Environment is created for the call, and its external lexical Environment reference is taken from counter.[[Environment]] :
Now, when the code in counter() looks for the count variable, it searches first for its own lexical environment (empty because there are no local variables there), then for the lexical environment of the external makeCounter(), and changes where it finds it.
Update variables in their lexical environment. So change the value of the count variable directly in makeCounter’s lexical environment.
This is the state after execution:
If we call counter() more than once, the count variable increases to 2,3 at the same place. This reference is permanently set, so it’s the same copy, because all we find is the lexical context of makeCounter(). This is how closure data persistence works.
Functions in JavaScript automatically remember where they were created via the hidden [[Environment]] property, so they all have access to external variables.
In interviews, front-end developers are often asked “What is a closure?” The correct answer should be the definition of closures, explaining why all functions in JavaScript are closures, and possible technical details about the [[Environment]] property and lexical Environment principles.
For closures, click in.
Hopefully, you’ll have a better understanding of scope chains, declaration promotions, closures, and more!
The Variable Environment
The variable environment is also a lexical environment, so it has all the attributes of the lexical environment defined above.
In ES6, the difference between the LexicalEnvironment component and the VariableEnvironment component is that the former is used to store function declarations and variable (let and const) bindings, while the latter is only used to store variable (var) bindings.
Not to mention var too much, but let/const generates block-level scope
conclusion
The execution context creation process is the process of preparing the runtime environment when JavaScript code is parsed and executed. The execution context is an abstraction of the code execution environment.
other
Interpretive OR compiled
We usually classify Javascript a “dynamic” and “interpretation” language, in fact, he is a hybrid language (and traditional compiling distinguishing, not compiled in advance, and compile the result cannot transplantation) in the distributed system, the traditional translation process, program before performing a source goes through three steps are collectively referred to as’ compiled ‘
- Word segmentation/lexical analysis
- This process breaks down a string of characters into blocks of code that make sense (to the programming language), called lexical units.
- Parsing/parsing
- This process converts a stream of lexical units (arrays) into a hierarchical nested tree of elements that represents the syntactic structure of the program. This Tree is called an Abstract Syntax Tree (AST).
- Optimization/code generation
- The process of turning an AST into executable code is called code generation
The variable object VO and the active object AO
These are the historical statements in ES3 if you’re interested
reference
- You don’t know javascript
- zh.javascript.info/closure
- Juejin. Cn/post / 684490…
- Github.com/mqyqingfeng…
- Segmentfault.com/a/119000001…
- Juejin. Cn/post / 684490…