在《深刻学习js之——执行上下文栈》中说过,当JavaScript代码执行一段可执行代码(executable code)时,会建立对应的执行上下文(execution context)函数
对于每个执行上下文,都有三个重要的属性:post
变量对象(Variable object VO)
做用域链(Scope chain)
this
本文咱们结合着这三个部分的内容,讲讲执行上下文的具体处理过程。学习
在《深刻学习js之——词法做用域和动态做用域》中,提出这样一道思考题:this
// 思考题一: var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope(); // 思考题二: var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
两段代码都会打印local scope
,可是仍是有些许差别的,本文就详细的解析执行上下文栈和执行上下文的具体变化过程。code
咱们分析第一段代码:对象
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();
执行过程以下:ip
一、执行全局代码,建立全局执行上下文,全局上下文被压入执行上下文栈作用域
ECStack = [ globalContext ];
二、全局上下文初始化get
globalContext = { VO: [global], Scope: [globalContext.VO], this: globalContext.VO }
二、初始化的同时,checkscope 函数被建立,保存做用域链到函数内部的属性[[scope]]
io
checkscope.[[scope]] = [ globalContext.VO ];
三、执行checkScope 函数,建立checkScope 函数执行上下文,checkScope 函数执行上下文被压入执行上下文栈:
ECStack = [ checkscopeContext, globalContext ];
四、checkscope 函数执行上下文初始化:
1.复制函数 [[scope]] 属性建立做用域链,
2.用 arguments 建立活动对象,
3.初始化活动对象,即加入形参、函数声明、变量声明,
4.将活动对象压入 checkscope 做用域链顶端,
同时 f 函数被建立,保存做用域链到 f 函数的内部属性[[scope]]
checkscopeContext = { AO: { arguments: { length: 0 }, scope: undefined, f: reference to function f(){} }, Scope: [AO, globalContext.VO], this: undefined }
五、执行f函数,建立 f 函数执行上下文,f 函数执行上下文被压入执行上下文栈
ECStack = [ fContext, checkscopeContext, globalContext ]
六、f 函数执行上下文初始化, 如下跟第 4 步相同:
1.复制函数[[scope]]
属性建立做用域链
2.用 arguments 建立活动对象
3.初始化活动对象,即加入形参、函数声明、变量声明
4.将活动对象压入 f 做用域链顶端
fContext = { AO: { arguments: { length: 0 } }, Scope: [AO, checkscopeContext.AO, globalContext.VO], this: undefined }
七、f 函数执行,沿着做用域链查找 scope 值,返回 scope 值
八、f 函数执行完毕,f 函数上下文从执行上下文栈中弹出
ECStack = [ checkscopeContext, globalContext ]
九、checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
ECStack = [ globalContext ]