What is an execution context
An execution context is an abstraction of the context in which JavaScript code is evaluated and executed. Whenever JavaScript code is running, it is running in an execution context.
Execution context type
- Global execution context:This is the default context, and any code that is not inside the browser is in the global context. There is only one global execution context in a program that does two things: 1. Create a global Window object (in the case of the browser); 2. Set 2.
this
For this global object. - Function execution context:Whenever a
Function called
A new context is created for the function. Each function has its own execution context, but is created when the function is called. Function contexts can have as many as they want, and each time a new execution context is created, it performs a sequence of steps in a defined order. - The Eval function execution context:To perform the
eval
The code inside a function also has its own execution context
Execution stack
An execution stack, or “call stack” in other programming languages, is a stack with LIFO (LIFO) data structures that are used to store all execution contexts created while the code is running.
When the JavaScript engine first encounters your script, it creates a global execution context and pushes it onto the current execution stack. Every time the engine encounters a function call, it creates a new execution context for that function and pushes it to the top of the stack.
The engine executes functions whose execution context is at the top of the stack. When the function completes execution, the execution context pops out of the stack and the control flow moves to the next context in the current stack.
Example:
let a = 'Hello World! '; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context');Copy the code
- When the above code is loaded by the browser, the JavaScript engine creates a global execution context and pushes it onto the current execution stack.
first()
When called, the JavaScript engine creates a new execution context for the function and pushes it to the top of the current execution stack.first()
Within thesecond()
When called, the JavaScript engine issecond()
The function creates a new function execution context and pushes it to the top of the stack.second()
When the execution is complete, its execution context pops up from the current execution stack and controls the flow to the next execution context, i.efirst()
The execution context of a function.first()
When the execution is complete, its execution context pops out of the stack and the control flow reaches the global execution context. Once all code has been executed, the JavaScript engine removes the global execution context from the current stack.
How do I create an execution context?
Now that we’ve looked at how JavaScript manages execution contexts, let’s look at how the JavaScript engine creates execution contexts.
There are two phases to creating an execution context: 1) the creation phase and 2) the execution phase.
Create a stage
Before the JavaScript code executes, the execution context goes through the creation phase. Three things happen during the creation phase:
- This binding
- Create the lexical environment component
- Create the variable environment component
So the execution context is conceptually expressed as follows:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }},Copy the code
This binding
In the context of global execution, the value of this points to a global object. In browsers, this refers to the Window object.
In the context of function execution, the value of this depends on how the function is called. If it is called by a reference object, this is set to that object, otherwise this is set to global or undefined (in strict mode).
Lexical environment
Before introducing Lexical Environment, let’s take a look at JS compilation and execution in V8, which can be roughly divided into three stages:
- Step one: JUST got the V8
Execution context
We will Tokenizing/Lexing the code from top to bottom line. Participle means: for exampleVar a = 2;
This code will be partitioned as:var
a
2
and;
Such atomic tokens; Lexical analysis refers to registering variable declarations, function declarations, and parameters of function declarations. - Step 2: At the end of the word segmentation, the code is parsed, and the engine translates the token parsing into an AST(abstract syntax tree). In this step, if a syntax error is found, an error will be reported and no further execution will be performed.
var greeting = "Hello"; console.log(greeting); greeting = ."Hi"; // SyntaxError: unexpected Token. // The JS engine will parse the code before actually executing it.Copy the code
- Step 3: The engine generates machine code that the CPU can execute.
The first step is a Lexical analysis, which registers variable declarations, function declarations, and function declarations’ parameters, and then the code executes knowing where to take the variable values and functions, which is the Lexical Environment.
Inside the lexical environment there are two components:
- Environment RecordThat’s where you actually register variables;
- Declarative Environment Record: Used to Record elements that are directly defined by identifiers, such as variables, constants, lets, classes, modules, imports, and function declarations.
- Object Environment Record: Lexical Environment used mainly for with and global.
- A reference to the outer lexical environment (OUTER), which is key to the chain of scopes being able to be linked together.
There are two types of Declarative Environment Record:
- Function Environment Record: used in Function scope.
- Module Environment Record: The Module Environment Record is used to reflect the external scope of a Module, that is, the Environment where the Module export resides.
The lexical environment corresponds to the structure of our own code, that is, the lexical environment is the way our own code is written. The lexical environment is determined when the code is defined, regardless of where the code is called. So JavaScript uses lexical scope (static scope).
Example:
var a = 2;
let x = 1;
const y = 5;
function foo() {
console.log(a);
function bar() {
var b = 3;
console.log(a * b);
}
bar();
}
function baz() {
var a = 10;
foo();
}
baz();
Copy the code
Its lexical context diagram is as follows:
We can use pseudocode to simulate the lexical environment of the above code:
GlobalEnvironmentRecord = {outer: null, GlobalEnvironmentRecord = {outer: null, GlobalEnvironmentRecord: {// The global this binding points to the global object [[GlobalThisValue]]: ObjectEnvironmentRecord [[BindingObject]], / / declarative environmental records, in addition to global function and the var, other declarations are binding DeclarativeEnvironmentRecord here: {x: 1, and y: }, // ObjectEnvironmentRecord: {a: 2, foo:<< function>>, baz:<< function>>, isNaNl:<< function>>, isFinite: << function>>, parseInt: << function>>, parseFloat: << function>>, Array: << construct function>>, Object: << construct function>> ... . }}} / / foo function lexical environment fooFunctionEnviroment = {outer: GlobalEnvironment, / / external environment of lexical reference point to the global environment FunctionEnvironmentRecord: << function>>}} // barFunctionEnviroment = {outer: FooFunctionEnviroment, / / external environment of lexical reference point to lexical environment FunctionEnvironmentRecord foo function: {[[ThisValue]] : GlobalEnvironment,//this binding refers to the GlobalEnvironment b: 3}} //baz functionenviroment = {outer: GlobalEnvironment, / / external environment of lexical reference point to the global environment FunctionEnvironmentRecord: {[[ThisValue]] : GlobalEnvironment, / / this binding to the global environment a: 10}}Copy the code
The variable environment
It is also a lexical environment whose environment logger holds bindings created in the execution context of variable declaration statements.
As mentioned above, the variable environment is also a lexical environment, so it has all the attributes of the lexical environment defined above.
In ES6, one difference between a lexical environment component and a variable environment is that the former is used to store function declarations and variable (let and const) bindings, while the latter is used only to store var variable bindings. Example:
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
Copy the code
We use pseudocode to describe the creation of an execution context in the above code:
ThisBinding: <Global Object>, // LexicalEnvironment: // EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // EnvironmentRecord: {// EnvironmentRecord: {Type: "Object", // < uninitialized >, multiply: < func >} // Global environment external environment is imported as null outer: <null>}, VariableEnvironment: {EnvironmentRecord: {Type: "Object", // Object environment record // identifier binding in here var created c in here c: undefined,} // global environment external environment introduced as null outer: <null>}} // Function execution context FunctionExectionContext = {// Since the function is called by default this binding is also the global object ThisBinding: <Global Object>, // LexicalEnvironment: {EnvironmentRecord: {Type: "Declarative", // Declarative environment record // identifier binding here Arguments object here: {0:20, 1:30, length: </Global> outer: <GlobalEnvironment>}, VariableEnvironment: {EnvironmentRecord: {Type: </Global> outer: <GlobalEnvironment>}} </Global> outer: <GlobalEnvironment>}Copy the code