深入理解JavaScript系列(6):执行上下文

《深入理解 JavaScript 系列(2):执行上下文栈》中提到,当 JavaScript 代码执行代码时,就会创建一个执行上下文。

对于执行上下文,都有三个重要属性:

  • 变量对象 (Variable Object - VO)
  • 作用域链
  • this

分别单独介绍了三个属性 - 《深入理解 JavaScript 系列(3):变量对象》《深入理解 JavaScript 系列(4):作用域链》《深入理解 JavaScript 系列(5):this》

我们在上面的文章中,不止一次提到过执行上下文这个东西,然后每次都是分开介绍的,并不完全,现在我们就来整合一下执行上下文吧。

执行上下文 - 例子一

我们直接给出一个例子吧,然后来看看什么是执行上下文吧。

1
2
3
4
5
6
7
8
9
var name = 'hzq';
function checkName() {
var name = 'hzzzzzzzq';
function fn() {
return name;
}
return fn();
}
console.log(checkName()); // hzzzzzzzq

执行过程

1. 执行全局代码,创建全局上下文,并压入执行上下文栈

1
ECStack = [globalContext];

2. 全局上下文初始化,同时创建 checkName 函数,保存作用域链到函数内部属性 [[scope]]

1
2
3
4
5
6
7
8
globalContext = {
VO: [global],
Scope: [globalContext.VO],
this: globalContext.VO,
};
checkName.[[scope]] = [
globalContext.VO
];

3. 执行 checkName 函数,创建函数上下文,并压入栈

1
ECStack = [checkNameContext, globalContext];

4. checkName 函数执行上下文初始化: | 这时候 fn 函数也被创建,保存 [[scope]]

  • 复制函数 [[scope]] 属性
  • arguments 创建活动对象
  • 初始化活动对象,加入形参、函数声明、变量声明
  • 将活动对象压入函数作用域链顶端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
checkNameContext = {
AO: {
arguments: {
length: 0,
},
name: undefined,
fn: reference to function fn() {}
},
Scope: [AO, globalContext.VO],
this: undefined
};

// fn 函数保存 [[scope]]

fn.[[scope]] = [
checkNameContext.AO, gloablContext.VO
]

5. 执行 fn 函数,创建上下文,压入栈

1
ECStack = [fnContext, checkNameContext, globalContext];

6. fn 函数执行上下文初始化

1
2
3
4
5
6
7
8
9
fnContext = {
AO: {
arguments: {
length: 0,
},
},
Scope: [AO, checkNameContext.AO, globalContext.VO],
this: undefined,
};

7. fn 通过作用域链查找 name 属性,并返回

8. fn 函数指向完毕,出栈

1
ECStack = [checkNameContext, globalContext];

9. checkName 函数执行完毕,出栈

1
ECStack = [globalContext];

10. 继续执行其他全局代码,直到其他代码执行完毕,全局上下文出栈

执行上下文 - 例子二

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'hzq';
function checkName() {
var name = 'hzzzzzzzq';
function fn() {
return name;
}
return fn;
}
checkName()(); // hzzzzzzzq
// 等价于
let fn = checkName();
fn();

执行过程 - 变化点

我们直接来看看例子二与例子一的不同点。

5. checkName 函数执行完毕,出栈

1
ECStack = [globalContext];

6. 执行 fn 函数,创建上下文,压入栈

1
ECStack = [fnContext, globalContext];

7. fn 函数执行上下文初始化

1
2
3
4
5
6
7
8
9
fnContext = {
AO: {
arguments: {
length: 0,
},
},
Scope: [AO, checkNameContext.AO, globalContext.VO],
this: undefined,
};

8. fn 函数执行完毕,出栈

9. 执行全局其他代码,执行完毕,全局上下文出栈

是不是很简单呢?

-------------------- 本文结束 感谢阅读 --------------------