之前的错误想法:
知道js有预编译阶段,但是一直以为预编译只执行一次,也就是在代码执行之前。
看了伢羽的博客之后,才知道,原来js代码是一段一段执行的,这里的一段指的是函数、全局或者eval
每一段的过程都包括预编译和执行阶段,而且是在全局代码执行的时候,才会进行全局函数的预编译哦。
让我们以下面这段代码为例,看看js引擎是怎么工作的?
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
所有段(函数、全局、eval)代码的执行过程分为两部分:预编译和执行
预编译包括:创建执行上下文、将执行上下文压入栈、执行上下文初始化(创建变量对象VO、this、作用域链)
- 全局代码执行前的准备工作(预编译)
1)创建全局执行上下文对象globalContext,并对它进行初始化
2)将全局执行上下文对象压入执行栈
globalContext = {
VO:{
scope:undefined(变量声明提升)
checkscope:reference to checkscope(){}(函数声明提升)
}
// 作用域链
scope:[globalContext.VO]
// 执行时才会确认
this:undefined
}
ECStack = [
globalContext
];
VO:varibale object--变量对象
初始化全局上下文对象时,经历了checkscope函数声明,所以checkscope函数的[[scope]]属性此时等于:
checkscope.[[scope]] = [globalContext.VO]
2.执行全局代码
此时对globalContext进行赋值。
globalContext = {
VO:{
scope:'global scope'
checkscope:reference to checkscope(f)
}
scope:[globalContext.VO]
this: globalContext
}
执行全局代码时,遇到checkscope函数执行声明,此时进入checkscope函数的预编译过程
- checkscope预编译
1)创建checkscope执行上下文对象checkscopeContext
2)将其压入执行上下文栈,并对其进行初始化
checkscopeContext = {
VO:{
arguments: {
length: 0
},
scope:undefined
f:reference to f(){}
}
scope:[VO,globalContext.VO]
this:undefined
}
ECStack = [
checkscopeContext,
globalContext
];
checkscope初始化的过程中,遇到f函数的函数声明,此时
f[[scope]] = [checkscopeContext.VO,globalContext.VO]
- checkscope真正执行
赋值操作
checkscopeContext = {
VO:{
arguments: {
length: 0
},
scope:"local scope"
f:reference to f(){}
}
scope:[VO,globalContext.VO]
this:globalContext.VO
}
checkscope执行的时候
- f函数预编译
创建f函数的执行上下文,并对其初始化
将f函数执行上下文对象压入执行上下文栈
fContext = {
VO:{
arguments: {
length: 0
},
scope:undefined
}
scope:[VO,checkscopeContext.VO,globalContext.VO]
this:undefined
}
ECStack = [
fContext,
checkscopeContext.VO
globalContext
];
- 执行f函数
赋值操作
fContext = {
VO:{
arguments: {
length: 0
},
scope:undefined
}
scope:[VO,checkscopeContext.VO,globalContext.VO]
this:globalContext
}
执行f函数时,遇到scope变量,在f函数VO中没找到该变量,沿着作用域链向上查找,在checkscopeContext的VO中找到,并返回'local scope'
7.弹出fContext checkscopeContext
8.弹出globalContext
9 结束执行
再尝试分析以下代码: