Sequential execution?
Let’s start with these two pieces of code
var foo = function () {
console.log('foo1');
}
foo(); // foo1
var foo = function () {
console.log('foo2');
}
foo(); // foo2
Copy the code
function foo() {
console.log('foo1');
}
foo(); // foo2
function foo() {
console.log('foo2');
}
foo(); // foo2
Copy the code
From the above two pieces of code, we can see the obvious difference, and the printed results are different. Anyone who has scanned an interview question knows that this is because JavaScript engines don’t analyze execution line by line. When a line of code is executed, a “prep” is done, such as a variable promotion in the first example and a function promotion in the second example.
But what this article really wants us to think about is: how is this “paragraph by paragraph” divided?
What kind of code does a JavaScript engine “prepare” for?
Executable code
Which brings us to what types of JavaScript executable code are there?
There are three simple ones: global code, function code, and eval code
For example, when a function is executed, there is preparation, which is, to use a more technical term, an “execution context.”
There are also three types of execution context:
Global execution context: It does two things: 1. Creates a global object, which in the browser is the Window object. 2. Point the this pointer to the global object. Only one global execution context can exist in a program
Function execution context: Each time a function is called, a new execution context is created for that function. Each function has its own execution context, but is created only when the function is called. Any number of function execution contexts can exist in a program. Each time a new execution context is created, it performs a series of steps in a specific order, as discussed later in this article.
Eval execution context: Code running in the Eval function also gets its own execution context, but since Eval is not often used by JavaScript developers, it is not covered here.
The lifecycle of the execution context
The execution context life cycle consists of three phases: create phase -> execute phase -> reclaim phase
- Create a stage
When a function is called, but before any of its internal code is executed, it does three things:
- Create a variable object: first initialize the function arguments and promote the function declaration and variable declaration.
- Create Scope Chain: During the creation phase of the execution context, the Scope Chain is created after the variable object. The scope chain itself contains objects. Scope chains are used to resolve variables. When asked to parse a variable, JavaScript always starts at the innermost layer of code nesting, and if the inner layer doesn’t find the variable, it jumps up to the parent scope until the variable is found.
- Make sure this refers to: including multiple cases.
Before a JAVASCRIPT script is executed, the code must be parsed. When parsing, a global execution context is created, and variables and function declarations are extracted from the code to be executed. The variable is temporarily assigned to undefined, and the function is declared ready to use. This step is done, and then the formal execution of the procedure begins.
In addition, before a function is executed, a function execution context is created, just like the global context, except that the function execution context includes this arguments and function arguments. In the execution phase, variable assignment and code execution are performed. 3. In the reclamation phase, the execution context is removed from the stack and waits for the VM to reclaim the execution context
Execution context stack
We’ve written so many functions, how do we manage so many execution contexts?
So the JavaScript engine creates an Execution Context stack (ECS) to manage the Execution context
To simulate the behavior of the execution context, let’s define the execution context stack as an array:
ECStack = [];
Copy the code
When JavaScript starts to interpret execution code, the first thing it encounters is global code, so initialization pushes a global execution context onto the execution context stack, which we call a globalContext, and only when the entire application ends, The ECStack is cleared, so there is always a globalContext at the bottom of the ECStack before the program ends:
ECStack = [
globalContext
];
Copy the code
When a function is executed, an execution context is created and pushed onto the execution context stack. When the function is finished, the execution context of the function is ejected from the stack. With that in mind, let’s see what we can do with the code above:
// pseudocode // fun1() ecstack.push (<fun1> functionContext); Ecstack.push (<fun2> functionContext); // fun2 also calls fun3! ECStack.push(<fun3> functionContext); Ecstack.pop (); Ecstack.pop (); Ecstack.pop (); // Javascript then executes the following code, but there is always a globalContext at the bottom of the ECStackCopy the code
Example:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
Copy the code
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
Copy the code
The execution context stack changes differently for these two pieces of code:
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
Copy the code
The second code:
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
Copy the code