js运行流程
js是解释型语言,解释一行执行一行,但是在执行之前会有一些预处理的操作,js的运行流程如下:
-
词法分析
词法分析,构建抽象语法树(AST),这一步能检查代码的语法错误,如果在构建过程中发现错误,会终止此次编译,直接抛出报错
-
预编译
-
解释执行
预编译
变量对象
预编译分为全局预编译和局部预编译,在预编译的过程中,会生成变量对象,变量对象是当前代码段中所有变量(形参、变量、函数)的集合,分为全局变量对象GO,和局部变量对象AO
全局预编译
发生时机:页面加载完成时执行
预编译步骤:
- 生成GO对象
- 找var声明的变量,变量名作为GO的属性名,值为undefined(变量提升)
- 找函数声明,函数名称作为GO对象的属性名,值为函数,和var声明的变量同名,直接覆盖变量(函数提升)
比如:
console.log('a',a) // a undefined
console.log('b',b) // b functionb(){}
a=1
function b(){}
var a
GO对象
局部预编译
发生时机:局部预编译发生在函数执行前
预编译步骤:
- 生成AO对象
- 找形参和var声明的变量,形参名和变量名作为AO的属性名,值为undefined(变量提升)
- 实参和形参相统一
- 找函数声明,函数名称作为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对象
函数声明和变量声明同名情况
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)
结论
从预解析的步骤可以看出,变量提升发生在函数提升之前,提升时,同名的函数会覆盖变量,所以函数提升的优先级会高于变量提升
关于函数提升和变量提升的更多细节,可以看我的另一篇文章 juejin.cn/post/724040…