深入js之执行上下文与预编译

143 阅读2分钟

之前的错误想法:

知道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. 全局代码执行前的准备工作(预编译)

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函数的预编译过程

  1. 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]

  1. checkscope真正执行

赋值操作


checkscopeContext = {
    VO:{
        arguments: {
            length: 0
        },
        scope:"local scope"
        f:reference to f(){}
    }
    scope:[VO,globalContext.VO]
    this:globalContext.VO
}

checkscope执行的时候

  1. f函数预编译

创建f函数的执行上下文,并对其初始化

将f函数执行上下文对象压入执行上下文栈


fContext = {
    VO:{
        arguments: {
            length: 0
        },
        scope:undefined
    }
    scope:[VO,checkscopeContext.VO,globalContext.VO]
    this:undefined
}
ECStack = [
    fContext,
    checkscopeContext.VO
    globalContext
];
  1. 执行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 结束执行

再尝试分析以下代码: