May 31, 2021 Article blog
The article comes from the public number: front-end Taoyuan, author Tao Ong
I've been studying
JavaScript
foundational stuff recently, but seeing the interpretation of
the execution context,
I've found that there are two kinds of execution contexts:
scope
variable object
(variable object),
this value
( this value), and one that contains:
lexical environment
variable environment
(variable environment), and
this value
Later I read a lot of blogs and ES3 and ES5 specifications to understand that the first is the ES3 specification, the classic book "JavaScript Advanced Program Design" the third edition is explained in this way, is also widely circulated on the Internet one, the other is the ES5 specification.
Then I went on to turn over ES2018, found that there has been a change, has added more content, considering this part of the content is quite complex, ready to be followed by a summary to share, check the information to see this talk about the execution context (ES5) is good, so translate it out and share it with everyone first.
Later see variable objects, active objects know is the content of ES3, and if it is lexical environment, variable environment this word is the content after ES5.
Here's the body:
In short, execution context is an abstract concept of the environment in which JavaScript code is calculated and executed. Whenever JavaScript code is running, it runs in the execution context.
There are three execution context types in JavaScript.
this
to equal the global object.
There is only one global execution context in a program.
eval
function also has its own execution context, but since JavaScript developers don't use
eval
very often, I won't discuss it here.
The execution stack, also known as the "call stack" in other programming languages, is a data structure that has a LIFO (last in, first out) and is used to store all execution contexts created by the code runtime.
When the JavaScript engine first encounters your script, it creates a global execution context and presses into the current execution stack. Whenever the engine encounters a function call, it creates a new execution context for the function and presses it into the top of the stack.
The engine executes functions in the execution context at the top of the stack. When the function execution ends, the execution context pops up from the stack and the control process reaches the next context in the current stack.
Let's understand this through the following code example:
let a = 'Hello World!';
functionfirst() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
functionsecond() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
When the above code is loaded in the browser,
the JavaScript
engine creates a global execution context and presses it into the current execution stack.
When a
first()
function call is encountered, the
JavaScript
engine creates a new execution context for the function and presses it into the top of the current execution stack.
When
first()
function is called from inside the
second()
function, the JavaScript engine creates a new execution context for
second()
function and presses it into the top of the current execution stack.
When
second()
function is executed, its execution context pops up from the current stack and the control process reaches the next execution context, the execution context of the
first()
function.
When
first()
is executed, its execution context pops up from the stack and the control process reaches the global execution context.
Once all the code is executed, the
JavaScript
engine removes the global execution context from the current stack.
Now that we've seen how JavaScript manages execution contexts, let's see how the JavaScript engine creates execution contexts.
There are two stages to creating an execution context: 1) to create and 2) to execute.
The execution context goes through the creation phase before javaScript code is executed. There are three things that happen during the creation phase:
So the execution context is conceptually like this:
ExecutionContext = {
ThisBinding = <this value>,
LexicalEnvironment = { ... },
VariableEnvironment = { ... },
}
In the context of global execution, the value of
this
points to the global object.
(In the browser,
this
refers to the Window object).
In the context of function execution, the value of
this
depends on how the function is called. I
f it is called by a reference object,
this
is set to that object, otherwise
this
value of this is set to a global object or
undefined
(in strict mode).
For example:
let foo = {
baz: function() {
console.log(this);
}
}
foo.baz(); // 'this' 引用 'foo', 因为 'baz' 被
// 对象 'foo' 调用
let bar = foo.baz;
bar(); // 'this' 指向全局 window 对象,因为
// 没有指定引用对象
The official ES6 document defines the lexical environment as
The lexical environment is a canonical type that defines the association of identifiers with specific variables and functions based on the lexical nesting structure of the ECMAScript code. A lexical environment consists of an environmental logger and a possible reference to the empty value of the outer lexical environment.
Simply put, the lexical environment is a structure that holds an identifier-variable map. (The identifier here refers to the name of the variable/function, and the variable is a reference to the actual object , which contains the function type object, or the original data.)
There are now two components inside the lexical environment: (1) an environment logger and (2) a reference to an external environment.
The external environment is already similar to the scope specified in ES3
There are two types of lexical environments:
this
points to the global object.
There are also two types of environmental loggers (e.g. above!) ):
nutshell
Note -
For
a function environment,
the
declarative environment logger
also contains an arguments object passed to the function (which
arguments
a map of indexes and parameters) and
a length
of the argument passed to the function.
Abstractly speaking, the lexical environment looks like this in pseudo-code:
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
}
outer: <null>
}
}
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
}
outer: <Global or outer function environment reference>
}
}
It is also a lexical environment whose environment logger holds a binding relationship created in the execution context by a variable declaration statement.
As mentioned above, a variable environment is also a lexical environment, so it has all the properties of the lexical environment defined above.
In ES6, a difference between the
lexical environment
component and the
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.
Let's look at the sample code to understand the above concept:
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
The execution context looks like this:
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
Note
- The function execution context is created only when the function
multiply
is encountered.
You may have noticed that
let
variables defined by let and
const
are not associated with any values, but the variables defined by
var
are set to
undefined
This is because during the creation phase, the engine checks the code to find variables and function declarations, which are initially set to
undefined
var
of var) or not initialized
let
case of let and
const
although the function declarations are stored entirely in the environment.
This is why you can access
var
variables (although
undefined
but
let
and
const
variables before the declaration results in a reference error.
This is what we mean by variable declaration elevation.
This is the simplest part of the whole article. At this stage, the allocation of all these variables is completed, and the code is executed at the end.
Note
- During the execution phase, if the JavaScript engine cannot find the value of the
let
variable at the actual location declared in the source code, it is assigned a value of
undefined
We've discussed how JavaScript is executed inside the program. While you don't need to learn all of these concepts to be a great JavaScript developer, having a good understanding of the above concepts will help you understand other concepts more easily and deeply, such as variable declaration elevation, scope, and closure.
That's what
W3Cschool编程狮
has learned about
(ES5 version) in-depth understanding of JavaScript execution context and execution stack, and
I hope it will help you.