The content is a summary of the blogs written by Changmiao and Frozen, and a little bit of my own experience. Hahahaha!
Scope
Definition :
1. Scope refers to the area where variables are defined in the program source code
2. The scope specifies how to find variables, that is, to determine the access rights of the currently executing code to the variables
Lexical scope and dynamic scope
JavaScript uses lexical scope (also called static scope), and the scope of a function is determined when the function is defined.
The opposite of lexical scope is dynamic scope. The scope of a function is determined when the function is called.
one example:
var a = 1;
function foo() {
console.log(a);
}
function bar() {
var a = 2;
foo();
}
bar(); //其结果是1
1. If JavaScript uses lexical scope, the execution process is:
Execute the foo function, first look for the local variable a inside the foo function, if not, look for the code at the level above it (var a = 1) according to the writing position, so it will print 1
2. If JavaScript uses dynamic scope, its execution process is:
Execute the foo function, first look for the local variable a inside the foo function, if not, look for the a variable from the bar function scope that calls the foo function, and look for the code above it (var a = 1), so Will print 2
Conclusion: The lexical scope of JavaScript
Execution context
Definition : Execution context is an abstract concept of the environment in which JavaScript code is evaluated and executed. Whenever JavaScript code is running, it runs in the execution context
JavaScript execution order
When JavaScript code is executed, the JavaScript engine does not analyze and execute the program line by line, but analyzes and executes it piece by piece. When a piece of code is executed, a "preparation work" is performed. So how is each piece of code that will be "prepared" divided?
Executable code
There are three types of JavaScript executable codes, namely:
1. Global code
2. Function code
3. Eval code (not commonly used)
For example, when a function is executed, "preparation work" will be performed. The "preparation work" here is more professionally called "execution context" here, which is called the function execution context.
Give an example to illustrate the global code and executable code~:
Global code
let str = 'hello world';
function foo() {
// 函数体中的代码不算全局代码
}
console.log(str); // hello world
Function code
function foo() {
console.log('I like JavaScript'); // 函数代码
console.log('hello world'); // 函数代码
}
Execution context stack
There are many functions in the code, so how should we manage so many execution contexts?
The JavaScript engine creates an Execution context stack (ECS) to manage the created execution context
The following is an example to simulate the behavior of the execution context stack~:
1. In order to simulate the behavior of the execution context stack, the execution context stack is an array:
ECStack = [];
2. When JavaScript starts to execute the interpreted code, the first encounter is the global code, so when it is initialized, a global execution context is pushed into the execution context stack. We use globalContext to represent it, and before the end of the program, this The global execution context will always exist.
ECStack = [
globalContext
];
Now we will simulate the behavior of the execution context with the following code block~:
function fun3() {
console.log('好困')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
When a function is executed, a context is created and pushed onto the stack, and when the function is executed, the execution context of the function will be removed from the stack.
// fun1()
ECStack.push(<fun1> functionContext);
// fun1中调用了fun2 还需要创造fun2的执行上下文
ECStack.push(<fun2> functionContext);
// fun2中调用了fun3 还需要创造fun3的执行上下文
ECStack.push(<fun3> functionContext);
// fun3执行完毕
ECStack.pop();
// fun2执行完毕
ECStack.pop();
// fun1执行完毕
ECStack.pop();
Create execution context
For each execution context, there are three important attributes:
1. Variable object (Variable object, VO)
2. Scope chain
3.this
Variable object
Definition : The variable object is the data scope related to the execution context, which stores the variable and function declarations defined in the context
Global context
The variable object of the global context is the global object window (browser)
Function context
In the context of the function, we use the activation object (AO) to represent the variable object (VO)
Activation object (AO): Before entering the execution phase, none of the attributes in the variable object (VO) can be accessed, but after entering the execution phase, the variable object (VO) is transformed into an active object (AO), and the attributes inside are all Can be accessed, and then start the execution phase of the operation. VO and AO are different names of the same object in different life cycles
Execution context
The execution context code will be processed in two stages:
1. Enter the execution context
2. Code execution
Give an example to illustrate the changes of AO in different stages:
function foo(a) {
var b = 2;
function c() {
}
var d = function() {
};
}
foo(1);
1. Enter the execution context
Variable objects will include:
1. All formal parameters of the
function 2. Function declaration
3. Variable declaration
AO at this time:
AO = {
arguments: {
// aruguments是一个对应传给函数的参数的类数组对象
0: 1, //arguments[0] = 1
length: 1 // arguments.length = 1
},
a: 1,
b: undefined,
c: reference to function c(){
},
d: undefined
}
2. Code execution
AO at this time:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c() {
},
d: reference to FunctionExpression "d"
}
Exercise
function foo() {
console.log(a);
a = 2;
}
foo(); // a is not defined
function bar() {
a = 2;
console.log(a);
}
bar(); // 2
//两端代码执行console的时候 AO:
AO = {
arguments: {
length: 0
}
}
函数中的 "a" 并没有通过 var 关键字声明,所有不会被存放在 AO 中。
但是由于第二段代码 a = 2; 位于 consol.log(a)之前,所以在执行console的时候全局变量a = 2
Scope chain
Definition : When looking up a variable, it will first look for the variable object of the current context. If it is not found, it will look for the variable object of the parent execution context, and always find the variable object of the global context, which is the global object. In this way, a linked list composed of multiple execution context variable objects is called a scope chain.
[[scope]] internal properties, when the function is created, all parent variables will be saved in it, which can be understood as a function [[scope]] is the hierarchical chain of all parent variable objects
For example~:
function foo() {
function bar() {
...
}
}
The respective [[scope]] above are:
foo.[[scope]] = [
globalContexts.VO
];
bar.[[scope]] = [
fooContexts.AO,
globalContexts.VO
];
Let us take an example to take a closer look at the creation process of the scope chain and variable objects in the context of function execution:
var scope = "global scope";
function checkscope() {
var scope2 = "local scope";
return scope2;
}
checkscop();
The execution process is as follows:
1. Create a checkscope function and save its scope chain to internal properties [[scope]]
checkscope.[[scope]] = [
globalContext.VO // 全局代码的变量对象
];
2. Execute the checkscope function, create the execution context of the checkscope function, and push the execution context of the checkscope function onto the stack
ECStack = [
checkscopeContext, //checkscope函数执行上下文
globalContext //全局执行上下文
];
3. The checkscope function is not executed immediately, start to make preparations, the first step: copy the function scope attribute to create a scope chain
checkscopeContext = {
Scope: checkscope.[[scope]], //将第一步函数的scope属性复制,创造作用域链
}
4. Step 2: Use arguments to create an active object, then initialize the active object, add formal parameters, function declarations, and variable declarations (that is, enter the execution context during context execution)
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: checkscope.[[scope]],
}
5. Push the active object into the top of the checkscope scope (that is, function activation)
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: [AO, globalConext.[[Scope]]]
}
6. Start executing the function and modify the attribute value of AO
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: 'local scope'
},
Scope: [AO, globalContext.[[Scope]]]
}
7. The function is executed and popped from the execution context stack
ECStack = [
globalContext
];
Closure
Definition: A closure is a function that has access to variables in the scope of another function
Example ~
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
Before we talk about closures, let us analyze this piece of code
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();
The execution process is as follows:
1. Encounter global code, create a global context, and push the global context onto the context stack
ECStack = [
globalContext
];
2. Global context initialization
globalContext = {
VO: [global],
Scope: [globalContext.VO],
}
3. At the same time of initialization, create a checkscope function and save its scope chain to the internal properties of the function [[scope]]
checkscope.[[scope]] = {
globalContext.VO
}
4. Execute the checkscope function, create a checkscope function execution context, and the checkscope execution context is pushed into the execution context stack
ECSstack = [
checkscopeContext,
globalContext
]
5.checkscope function execution context initialization
- Copy function [[scope]] attribute to create scope chain
checkscopeContext = {
scope:checkscope.[[scope]]
}
- Use arguments to create an active object, initialize the active object, add formal parameters, function declarations, and variable declarations
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,
f: reference to function f(){
}
},
}
- Push the active object to the top of the checkscope scope
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,
f: reference to function f(){
}
},
Scope: [AO, globalContext.VO],
this: undefined
}
6. The f function is created, and the scope is saved to the internal properties of the f function [[scope]]
fscope.[[scope]] = {
checkscopeContext.AO, globalContext.VO
}
7.f function initialization
- Copy function [[scope]] attribute creation scope
- Use arguments to create an active object, initialize the active object at the same time, add formal parameters, variable declarations, and function declarations
- Move the active object to the top of the f scope chain
fContext = {
AO: {
arguments: {
length: 0
}
},
Scope: [AO, checkscopeContext.AO, globalContext.VO],
}
8.f function execution, looking for scope along the scope chain
9.f function is executed, popped from the execution context stack
ECSstack = [
checkscopeContext,
globalContext
]
10. The checkscope function is executed and popped from the execution context stack
ECSstack = [
globalContext
]