(二)V8引擎如何执行JS代码

306 阅读3分钟

JS引擎工作流程

以下为v8引擎举例:

图片.png 说明:

  1. 解析器:将js进行解析和预解析,经过词法分析和语法分析后生成AST树;(不论解析阶段还是预解析阶段,这时候函数的作用域已经确定

    预解析:将不是立即执行函数的函数进行预解析,对其标记为函数,但不解析函数里面的代码,而对函数内部得解析是在函数被调用时才会进行。

  2. 解释器:将AST树转为字节码,并且执行字节码。执行的过程中会标记热点代码(比如执行多次的函数),会把热点代码的字节码传给编译器进行优化。

  3. 编译器:将热点代码的字节码转为机器指令并缓存下来,等解释器下次执行可以直接执行机器指令提高执行效率。 注意: 优化可以提高代码执行速度,但是某些优化会导致错误,比如当一个函数参数类型发生变化时,之前已经优化过的代码可能会出现类型错误,这时就需要进行逆优化,恢复到未优化的状态。



js从解析到运行的过程

注意:
a. 变量声明提升和函数声明提升是有先后顺序,先进行变量提升再进行函数提升;在提升的过程中,如果变量名称和函数名称重复的话,函数整体会替换掉变量。

b. 在函数内,如果出现了相同名称的函数声明形参声明,函数声明也会覆盖形参声明。

code解析和运行前的过程

下方全局代码和函数代码中,步骤一、二、三为解析时生成,步骤四和五为运行前生成。

全局代码情况如下

图片.png

函数代码情况如下

图片.png

function foo(a){
    console.log(a); //function a(){}
    var a = 2
    console.log(a); //2
    function a(){}
}

foo(1)


function foo(a){
    console.log(a); //1
    var a = 2
    console.log(a); //2
}

foo(1)

code运行时

图片.png

将全局执行上下文/函数执行上下文压入到执行上下文栈(Execution Context stack)中,从栈顶开始执行,执行上下文还会生成作用域链(scope chain),当代码执行时取值或赋值操作会在当前的VO进行寻找,寻找不到则会去父作用域的VO寻找,以此往上寻找,直到找到全局执行上下文的VO,若找不到则会报错。

code运行后

图片.png 会依次将函数执行上下文进行出栈操作


变量对象和变量环境

es6之前

每个执行上下文关联一个变量对象(veriable object,VO),在源代码中的变量和函数声明会被作为属性添加到VO中。对于函数来说,参数也会被作为属性被添加到变量对象中。而全局变量对象指向的是window/GO

es6

每个执行上下文关联一个变量环境(veriable environment,VE)中,在执行代码中变量和函数声明会被作为环境记录(Enviroment Record)添加到变量环境中。对于函数来说,参数也会被作为环境记录添加到变量环境中。而全局变量环境指向的是variable_