js预编译

95 阅读2分钟

js运行流程

js是解释型语言,解释一行执行一行,但是在执行之前会有一些预处理的操作,js的运行流程如下:

  1. 词法分析

    词法分析,构建抽象语法树(AST),这一步能检查代码的语法错误,如果在构建过程中发现错误,会终止此次编译,直接抛出报错

  2. 预编译

  3. 解释执行

预编译

变量对象

预编译分为全局预编译和局部预编译,在预编译的过程中,会生成变量对象,变量对象是当前代码段中所有变量(形参、变量、函数)的集合,分为全局变量对象GO,和局部变量对象AO

全局预编译

发生时机:页面加载完成时执行

预编译步骤:

  1. 生成GO对象
  2. 找var声明的变量,变量名作为GO的属性名,值为undefined(变量提升)
  3. 找函数声明,函数名称作为GO对象的属性名,值为函数,和var声明的变量同名,直接覆盖变量(函数提升)

比如:

        console.log('a',a) // a undefined
        console.log('b',b) // b functionb(){}
        a=1
        function b(){}
        var a

GO对象

image.png

局部预编译

发生时机:局部预编译发生在函数执行前

预编译步骤:

  1. 生成AO对象
  2. 找形参和var声明的变量,形参名和变量名作为AO的属性名,值为undefined(变量提升)
  3. 实参和形参相统一
  4. 找函数声明,函数名称作为AO的属性名,值为函数体,和var声明的变量同名,直接覆盖变量(函数提升)

比如:

        var a = 1
        function b(d){
            console.log(a) //undefined
            console.log(b) //undefined
            console.log(c) //function c(){}
            console.log(d) // 4
            var a=2
            if(false){
                var b =1
            }
            function c(){}
        }
        b(4)

AO对象

image.png

函数声明和变量声明同名情况

        function b(d){
            console.log(a) // function a(){}
            console.log(b) //undefined
            console.log(c) //function c(){}
            console.log(d) // 4
            if(false){
                var b =1
            }
            function c(){}
            a=2
            console.log(a) // 2
            function a(){}
            console.log(a) // 2


        }
        b(4)

image.png

结论

从预解析的步骤可以看出,变量提升发生在函数提升之前,提升时,同名的函数会覆盖变量,所以函数提升的优先级会高于变量提升

关于函数提升和变量提升的更多细节,可以看我的另一篇文章 juejin.cn/post/724040…