执行上下文
-
执行上下文生命周期
- 创建阶段
- 建立变量,函数,参数对象并给参数赋值
- 建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性值
- .检查当前上下文中的函数声明。每找到一个函数声明,就在VariableObject下面用函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用,如果上述函数名已经存在于VariableObject下,那么则忽略后面的函数声明
- 检查当前上下文中的变量声明。每找到一个变量声明,就在VariableObject下面用变量名建立一个属性,该属性值为undefined。如果变量属性名和函数属性名重复,则不建立新的变量。
- 建立作用域链
- 确定this的值
- 建立变量,函数,参数对象并给参数赋值
- 执行阶段
- 变量赋值
- 函数引用
- 执行其他代码
- 创建阶段
-
可以将执行上下文看做一个对象其中包含了三个属性
(executionContextObj = {
variableObject: { /* vo活动对象 */ },
scope:/*执行上下文的作用域链*/,
this: {}
}
- 上下文的建立阶段,先是处理参数声明,接着是函数的声明,最后是变量的声明
作用域链
- 作用域链在函数定义的时候就已经确定了,作用域链是和函数定义时的位置有关。函数创建的时候创建了一个外部对象的的作用域链存在函数内部的[[scoper]]属性中。函数在执行的时候则会创建一个执行上下文,然后通过复制函数对象中的[[scope]]属性来构建执行上下文中的作用域链,并且把自己的活动对象推向当前作用域链的前端以此形成完整的作用于链。[[scope]]属性中保存的是对课访问变量对象的引用,而不是值的复制。
闭包
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();
以上代码的执行过程如下
- 执行全局代码,创建全局执行上下文,全局上下文被压入执行上下文栈
ECStack = [
globalContext
];
// 全局的执行环节
- 全局上下文初始化
globalContext = {
VO: [global],
Scope: [globalContext.VO],
this: globalContext.VO
}
初始化的同时,checkscope 函数被创建,同时会将函数保存到全局上下文的vo对象中,checkscope 函数保存作用域链到函数的内部属性[[scope]]
3. 执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
ECStack = [
checkscopeContext,
globalContext
];
- checkscope 函数执行上下文初始化,同时 f 函数被创建,保存作用域链到 f 函数的内部属性[[scope]]:
- 复制函数 [[scope]] 属性创建作用域链,
- 用 arguments 创建活动对象,
- 初始化活动对象,即加入形参、函数声明、变量声明,
- 将活动对象压入 checkscope 作用域链顶端
- 这是checkscope函数的执行上下文:
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope: undefined,//定义的scope
f: reference to function f(){}
},
Scope: [AO, globalContext.VO],//作用域链
this: undefined
}
- checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
- 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈
fContext = {
AO: {
arguments: {
length: 0
}
},
Scope: [AO, checkscopeContext.AO, globalContext.VO],
this: undefined
}
- f 函数执行,沿着作用域链查找 scope 值,返回 scope 值
f 函数依然可以读取到 checkscopeContext.AO 的值,说明当 f 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。 - f 函数执行完毕,f 函数上下文从执行上下文栈中弹出
ECStack = [
globalContext
];