这个系列是我读冴羽老师博客的感悟,
加入了个人的解读和练习题的解答
本篇是3、4、5、6篇的汇总,会用两个例子来详细说明执行上下文的运行流程
例子1
1
2
3
4
5
6
7
8
9
|
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
|
1.执行全局代码,创建全局上下文,全局上下文压入执行上下栈
1
2
3
|
ECStack = [
globalContext
]
|
2.初始化全局上下文
1
2
3
4
5
|
globalContext = {
VO: [global],
scope: [globalContext.VO],
this: globalContext.VO
}
|
2.初始化的同时,创建checkScope函数,checkscope函数保存作用域链到函数内部属性
1
|
checkscope.[[scope]] = [globalContext.VO];
|
3.执行checkscope函数,创建checkScope执行上下文,压入执行上下文栈
1
2
3
4
|
ECStack = [
checkscopeContext,
globalContext
]
|
4.初始化checkScope执行上下文,
4.1复制checkscope函数的内部属性[[scope]]创建作用域链
4.2用arguments创建AO活动对象,
4.3初始化活动对象,即加入函数形参,函数声明,变量声明
4.4将活动对象压入checkscope的作用域顶端
1
2
3
4
5
6
7
8
9
10
11
|
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,
f: reference to fucntion f(){}
},
scope: [AO, globalContext.VO],
this: undefined
}
|
同时f函数被创建,保存作用域链到f函数内部属性[[scope]]
1
|
f.[[scope]] = [checkscopeContext.AO, globalContext.VO];
|
5.执行f函数,创建f函数执行上下文,将其压入执行上下文栈
1
2
3
4
5
|
ECStack = [
fContext,
checkscopeContext,
globalContext
]
|
6.f函数执行上下文初始化,
6.1复制函数[[scope]]属性创建作用域链
6.2用arguments创建活动对象
6.3舒适化活动对象,即加入形参,函数声明,变量声明
6.4将活动对象压入f作用域顶端
1
2
3
4
5
6
7
8
9
|
fContext = {
AO: {
arguments: {
length: 0
},
},
scope: [AO, checkScopeContext.AO, globalContext.VO],
this: undefined
}
|
7.执行函数f,沿着作用域链找到scope的值,返回scope的值
8.函数f执行完毕后,在ECStack中出栈
1
2
3
4
5
|
ECStack.pop()
ECStack = [
checkscopeContext,
globalContext
]
|
9.函数checkScope执行完毕后在ECStack出栈,
1
2
3
4
|
ECStack.pop()
ECStack = [
globalContext
]
|
例子2
例子2和例子很相似
1
2
3
4
5
6
7
8
9
|
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
|
1.开始执行全局代码,创建全局上下文,并将其压入上下文执行栈
1
2
3
|
ECStack = [
globalContext
]
|
2.初始化全局上下文
1
2
3
4
5
|
globalContext = {
VO: [global],
scope: globalContext.VO,
this: globalContext.VO
}
|
2.初始化的同时创建函数checkscope,函数checkScope保存作用域链到其内部属性[[scope]]
1
|
checkscope.[[scope]] = [globalContext.VO];
|
3.执行函数checkScope,创建checkScope函数的执行上下文,并将其压入上下文执行栈
1
2
3
4
|
ECStack = [
checkScopeContext,
globalContext
]
|
4.初始化checkScope执行上下文,
4.1保存函数的内部属性[[scope]]到执行上下文的作用域链
4.2根据函数的arguments创建活动对象AO
4.3初始化活动对象,即加入函数形参,函数声明,变量声明
4.4将活动对象压入checkscope作用域的顶端
1
2
3
4
5
6
7
8
9
10
11
|
checkScopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,
f: reference to fucntion f(){}
},
scope: [AO, globalContext],
this: undefined
}
|
4.同时创建函数f,保存作用域链到f函数内部属性[[scope]]
1
|
f.[[scope]] = [checkscopeContext.AO, globalContext.VO];
|
5.沿着作用域找到函数f的指向,返回函数f的指向,函数checkScope执行完毕后在ECStack出栈
1
2
3
4
|
ECStack.pop()
ECStack = [
globalContext
]
|
6.执行checkScope返回的函数f,创建函数f的执行上下文,将其压入执行上下文栈
1
2
3
4
|
ECStack = [
fContext,
globalContext
]
|
7.初始化f的执行上下文fContext
7.1复制函数内部属性scope到函数上下文的作用域链
7.2根据函数的arguments创建活动对象AO
7.3初始化活动对象,即加入函数形参,函数声明,变量声明
7.4将活动对象压入函数上下文作用域链的顶端
1
2
3
4
5
6
7
8
9
|
fContext = {
AO: {
arguments: {
length: 0
}
}
scope: [AO, checkScopeContext.AO, globalContext]
this: undefined
}
|
8.执行函数f,沿着作用域链找到scope的值(这里是在checkScopeContext.AO里找到scope的,即便checkScopeContext.AO已经在ECStack中出栈,这就是闭包的原理),返回scope的值
7.函数f执行完毕后,f的执行上下文从执行上下文栈中弹出
1
2
3
4
|
ECStack.pop()
ECStack = [
globalContext
]
|