# 执行上下文栈
javaScript 引擎不是一行行执行的,而是一段段执行的。当执行一段代码的时候,会进行一个 “准备工作”,比如变量提升(var 声明的变量)或函数提升(函数声明,非函数表达式),这个“准备工作”就是构建执行上下文。JavaScript 引擎创建执行上下文栈(ECS)来管理执行上下文。而重点是如何理解“一段段”是如何划分的?
实例1:
var foo = function () {
console.log('foo1');
}
foo(); // foo1
var foo = function () {
console.log('foo2');
}
foo(); // foo2
// 原因:存在 `var foo` 变量提升,函数表达式不会提升,实质代码如下:
var foo;
foo = function () {
console.log('foo1');
}
foo(); // foo1
foo = function () {
console.log('foo2');
}
foo(); // foo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实例2:
function foo() {
console.log('foo1');
}
foo(); // foo2
function foo() {
console.log('foo2');
}
foo(); // foo2
// 原因:存在函数提升,函数声明可以提升,实质代码如下:
var foo;
foo = function () {
console.log('foo1');
}
foo = function () {
console.log('foo2');
}
foo(); // foo2
foo(); // foo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
关于提升,函数创建有函数声明和函数表达式之分,函数表达式是不会被提升的, 函数声明存在变量提升。
// 函数表达式不会提升
foo(); // TypeError: undefined is not a function
var foo = function() {
console.log('foo');
}
// 函数声明可以提升
foo(); // foo
function foo() {
console.log('foo');
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# JavaScript 的可执行代码
- 全局代码(表达式,赋值语句等)
- 函数代码
- eval代码
JS 引擎在解释代码时,最先遇到全局代码,所以初始化的时候首先就会向执行上下文压入全局执行上下文(globalContext),并且只有当整个应用程序结束的时候,ECStack 才会被清空,所以程序结束之前,ECStrack 底部永远有一个 globalContext。
当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。(堆栈处理)
- 创建阶段,执行上下文会创建变量对象(VO),建立作用域链(scope chain),以及确定 this 的指向
- 代码执行阶段,创建完成后,才会开始执行代码,这个过程就是变量赋值(AO),函数引用,以及执行其他代码
实例1:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
实例2:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
函数执行结束之后,如果没有显示地返回值,默认是undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
← 词法作用域和动态作用域 变量对象 →